let num = dic["value"] as? Int
dic["name"] = "name"
推荐
struct Data {
let num: Int
var name: String?
}
let num = data.num
data.name = "name"
适合使用Dictionary
的场景
数据不使用
- 数据并不读取
只是用来传递。
解耦
- 1.组件间通信
解耦使用HashMap
传递参数进行通信。2.跨技术栈边界的场景,混合栈间通信/前后端通信
使用HashMap
/JSON
进行通信。
使用枚举关联值
代替Any
例如使用枚举改造NSAttributedString
API,原有APIvalue
为Any
类型无法限制特定的类型。
优化前
let string = NSMutableAttributedString()
string.addAttribute(.foregroundColor, value: UIColor.red, range: range)
改造后
enum NSAttributedStringKey {
case foregroundColor(UIColor)
}
let string = NSMutableAttributedString()
string.addAttribute(.foregroundColor(UIColor.red), range: range) // 不传递Color会报错
使用泛型
/协议关联类型
代替Any
使用泛型
或协议关联类型
代替Any
,通过泛型类型约束
来使编译器进行更多的类型检查。
使用枚举
/常量
代替硬编码
代码中存在重复的硬编码
字符串/数字,在修改时可能会因为不同步引发bug
。尽可能减少硬编码
字符串/数字,使用枚举
或常量
代替。
使用KeyPath
代替字符串
硬编码
KeyPath
包含属性名和类型信息,可以避免硬编码
字符串,同时当属性名或类型改变时编译器会进行检查。
不推荐
class SomeClass: NSObject {
@objc dynamic var someProperty: Int
init(someProperty: Int) {
self.someProperty = someProperty
}
}
let object = SomeClass(someProperty: 10)
object.observeva lue(forKeyPath: "", of: nil, change: nil, context: nil)
推荐
let object = SomeClass(someProperty: 10)
object.observe(.someProperty) { object, change in
}
内存安全
减少使用!
属性
!
属性会在读取时隐式强解包
,当值不存在时产生运行时异常导致Crash。
class ViewController: UIViewController {
@IBOutlet private var label: UILabel! // @IBOutlet需要使用!
}
减少使用!
进行强解包
使用!
强解包会在值不存在时产生运行时异常导致Crash。
var num: Int?
let num2 = num! // 错误
提示:建议只在小范围的局部代码段使用!
强解包。
避免使用try!
进行错误处理
使用try!
会在方法抛出异常时产生运行时异常导致Crash。
try! method()
使用weak
/unowned
避免循环引用
resource.request().onComplete { [weak self] response in
guard let self = self else {
return
}
let model = self.updateModel(response)
self.updateUI(model)
}
resource.request().onComplete { [unowned self] response in
let model = self.updateModel(response)
self.updateUI(model)
}
减少使用unowned
unowned
在值不存在时会产生运行时异常导致Crash,只有在确定self
一定会存在时才使用unowned
。
class Class {
@objc unowned var object: Object
@objc weak var object: Object?
}
unowned
/weak
区别:
weak
- 必须设置为可选值,会进行弱引用处理性能更差。会自动设置为nil
unowned
- 可以不设置为可选值,不会进行弱引用处理性能更好。但是不会自动设置为nil
, 如果self
已释放会触发错误.
错误处理方式
可选值
- 调用方并不关注内部可能会发生错误,当发生错误时返回nil
try/catch
- 明确提示调用方需要处理异常,需要实现Error
协议定义明确的错误类型
assert
- 断言。只能在Debug
模式下生效
precondition
- 和assert
类似,可以再Debug
/Release
模式下生效
fatalError
- 产生运行时崩溃会导致Crash,应避免使用
Result
- 通常用于闭包
异步回调返回值
减少使用可选值
可选值
的价值在于通过明确标识值可能会为nil
并且编译器强制对值进行nil
判断。但是不应该随意的定义可选值,可选值不能用let
定义,并且使用时必须进行解包
操作相对比较繁琐。在代码设计时应考虑这个值是否有可能为nil
,只在合适的场景使用可选值。
使用init
注入代替可选值
属性
不推荐
class Object {
var num: Int?
}
let object = Object()
object.num = 1
推荐
class Object {
let num: Int
init(num: Int) {
self.num = num
}
}
let object = Object(num: 1)
避免随意给予可选值默认值
在使用可选值时,通常我们需要在可选值为nil
时进行异常处理。有时候我们会通过给予可选值默认值
的方式来处理。但是这里应考虑在什么场景下可以给予默认值。在不能给予默认值的场景应当及时使用return
或抛出异常
,避免错误的值被传递到更多的业务流程。
不推荐
func confirmOrder(id: String) {}
// 给予错误的值会导致错误的值被传递到更多的业务流程
confirmOrder(id: orderId ?? "")
推荐
func confirmOrder(id: String) {}
guard let orderId = orderId else {
// 异常处理
return
}
confirmOrder(id: orderId)
提示:通常强业务相关的值不能给予默认值:例如商品/订单id
或是价格
。在可以使用兜底逻辑的场景使用默认值,例如默认文字/文字颜色
。
使用枚举优化可选