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开发工程师