Swift防御编程

1、背景

我们在代码设计和编写过程中,要尽可能的确保自己的代码不出错,防御性编程能使我们尽早的发现较小问题,而不是等到客户反馈或者收到异常崩溃的时候才发现。 作为开发人员,几乎没有人能保证自己的程序一定没有bug,所以适度的防御会提高开发质量并且降低调试bug的时间。

防御编码的有效方法: 一旦碰到约定的异常,程序上必须做兼容处理,一定不能让程序Crash 一旦碰到预定的异常,就抛出去,如果上层没有处理,则Crash

2、防御编程的习惯

  • 1.如果无必须,就不要新增实体或类:如果可以使用更少的东西做同样的事情,那么新增更多的变量或实体、则都需要精力维护,也更容易出问题。
  • 2.不要仓促的编写代码:开始着手写的每一行代码都需要三思而后行,要考虑可能会出现什么样的错误,考虑所有可能出现的逻辑分支。
  • 3.尽可能处理掉编译警告:处理编译警告是一种优秀的习惯,编译警告可能隐藏某种错误。
  • 4.检查所有返回值:检查方法或属性的返回值,防止发生异常。
  • 5.谨慎使用强制转换:强制转换需要考虑入参类型,谨慎处理,对强制转换尽可能说明清楚。
  • 6.检查数值的边界:对数值、数组的范围要做好判断,有很多历史崩溃教训都是由越界造成的。

3、Swift中一些需要注意的点

3.1:可选类型(Optional Type) 1.尽量避免声明隐式可选类型,除非能确定其使用时一定有值。 2.避免使用as! 或者 try! 强制转换,如果转换失败会发生Crash 应使用 as? 和 try? 。 3.尽慎对可选变量,使用强制解包,也就是谨慎使用 ! 。 可选变量的值,可能是nil,如果对可选变量使用强制解包,需要明确上下文环境,是否会出现nil。特别可能变量是类的储存属性,并且在多线程环境下使用,请务必不要使用强制解包。

var test: String?

print(test!)//❎

print(test ?? "")//✅

if let test2 = test {

print(test2)//✅

}

3.2多个运算符结合运算时,使用括号区分优先级

  • let result = true || false && false
  • let result2 = (true || false) && false
  • let result3 = true || (false && false)
  • 第一行:看不出谁的运算级谁高
  • 第二行:把||用括号括起来,增强或优先级
  • 第三行:把&&用括号括起来,增强与优先级
  • 打印结果
  • true
  • false
  • true
  • 可以看出&&的优先级高

3.3闭包中使用weak self 避免循环引用

self.resultBlock = {[weak self] in

guard let self = self else {return}

//Code: XXXXX

}

3.4防止数组越界

extension Array {
func safeObject(index: Int) -> Element? { if self.count > index { return self[index] } else { return nil } } } let array: [String?] = []
array.safeObject(index: 0)

3.5 OC默认非空声明对Swift的影响 OC的对象和方法里面的参数在Swift获取的时候,默认都是 隐式可选类型 。其值可能为nil,如果直接使用可能会使程序直接崩溃,所以在Swift内调用OC的方法或者属性的时候,一定要进行非空判断,防止Crash

3.6 Swift与OC混编时反射问题 在OC内,要根据类名获取Swift实体类的时候,要在类名前加工程名 例:Honey.TYView

3.7 Swift中String.count与OC中NSString.length不总是相同 Sring.count 和 NSString.length 当字符串里面包含emoji表情的时候。两个数可能会不相同。这就导致String.count 获取的不是真实的字符串长度。可以使用String.tyf16.count 或者转为NSString.length

4、Swift中的一些规范 1.尽可能的使用let 少使用var 2.在生命类方法的时候,优先使用static func 而不是 class func 3.如果函数没有入参,函数比较简单,可以考虑使用计算属性 var height: CGFloat {
return 3 * 0.5 } func height() -> CGFloat {
return 3 * 0.5 } 4.展开可选参数的时候,建议使用guard 而不是if 减少嵌套。 var test: String? guard let test = test else {return} //Code: xxx if let test = test { //Code: xxx } 5.数据模型,如果不需要多个地方改动,尽量使用Struct 避免使用Class 6.在可能出现错误的地方,使用断言(assert) var test: String? guard let test = test else { assert(true, "test 不合法") return } 7.在Swift里面合理使用extention。将网络请求,公共方法,私有方法,点击手势,代理等使用extention进行划分,使用区块注释。 8.代理属性前面要用weak修饰避免循环引用 9.在属性懒加载里面使用闭包,在闭包内部不能使用self调用该属性。 private lazy var button: TYButton = { let button = TYButton() button.didClick = {[weak self] in self?.button.alpha = 0 } return button }() 10.在方法和属性前面尽量声明公开类型和私有类型。 11.在函数内的参数,如果给参数设置默认值,在调用的时候可以不传入该参数 bindData(title: "title") bindData(title: "title", subTitle: "subTitle") func bindData(title: String?, subTitle: String = "") {

    }

12.如果函数的参数中有闭包,且只有一个闭包,尽量使用尾随闭包 bindData(title: "123") {

     }
     func bindData(title: String? ,finish:(() -> ())?) {
         finish?()
     }

作者介绍

  • 游成虎 高级iOS开发工程师

微鲤技术团队

微鲤技术团队承担了中华万年历、Maybe、蘑菇语音、微鲤游戏高达3亿用户的产品研发工作,并构建了完备的大数据平台、基础研发框架、基础运维设施。践行数据驱动理念,相信技术改变世界。