对象之间的通信
Block, Delegate, NSNotification, KVO 之间的区别
Block
- Block 本身是一个对象,这个对象包含了一段代码逻辑
- Block 由于作为一个对象,可以作为参数或者返回值,同时,本身也可以带输入参数
- Block 的内存分配位置位于栈
pros:
- 调用处定义,可以使得代码逻辑清晰,不需要代码跳转
- 可以为匿名,或存储在临时变量中,在有需要的时候调用
- GCD 使用了大量的 Block 来处理多线程的问题
cons:
- Block 会对所设计的对象进行 retain, 容易造成内存泄漏
Delegate
- Delegate 是 iOS SDK 中常用的设计模式
- 使用 Delegate 前,需要定义一个协议,协议中包含了被委托方(如 controller)在实现这个协议时必须实现或者可选实现的方法
pros:
- 语法约束严格,代码维护成本低,代码可读性较强
- 编译器辅助检查协议实现的情况,对于没有实现的委托方法,会出现警告或错误
- 被委托方可以实现多个协议,因此获得多项能力,同时可以依赖此项特性来获得类似多继承的特性
- 助于降低代码的耦合度
cons:
- 实现委托模式时,需要的代码量较大
- 在设置代理对象时,需要处理好委托方与被委托方之间的引用关系,否则容易造成引用循环
- 对于跨层传值通信的,会增加代码的复杂度以及耦合性
注意点:
- 当代理方法中的逻辑运行在子线程上时,回调的 UI 操作必须要切换到主线程
NSNotification
- 通过一个单例对象
NSNotificationCenter
进行协调
- 当某个事件发生时,可以对任意多个对象进行通信
pros:
- 代码量少,实现简单,使用形式简便:(post 通知)+ (注册通知,处理通知)
- 一个对象发出的通知,可以被多个对象接收,一对多形式的通信实现简单
- 传值方便,通过一个字典(context) 来进行传值
cons:
- 编译器不能检测代码中,通知是否被正确处理
- 需要记得去释放注册通知的对象,否则会出现 crash
- 调试时,难跟踪控制流程
- 监听者需要提前获知通知名称,以及传值时(context)中的键
- 被监听者发送通知时,并不能获得发送成功与否的反馈信息
- 对于复杂的监听关系,可能需要一个中介来管理监听者与被监听者之间的关系
KVO
- 对象 A 能够监听对象 B 中某个属性的值变化
- 相对于 NSNotification, 监听的粒度更细
pros:
- 简单的方法实现两个对象间的同步,如 model 与 view
- 对于 SDK 中提供的内部对象,不需要改变它们的内部实现,我们也可以进行监听
- 能够获取对象 B 中属性变化的新值与旧值
- 通过只用 KeyPath, 可以观察对象中的对象,即嵌套对象
cons:
- 被观察的属性多是使用的是字符串来进行指定,编译期间对错误不能提供及时的警告或错误信息
- 代码进行重构时,若属性名称发生了变化,那原来的观察代码将失效
- 对于观察者而言,实现 KVO 的方法只有一个,因此在观察多个属性时,代码可能会出现多个 if-else 的判断
- 观察者在释放前,需要记得需要移除监听
注意点:
- 对于同一个 keyPath 进行了两次或以上的
removeObserver
时,会导致 crash
- 当父类在
dealloc
时移除一次,子类 dealloc
时又移除一次,crash
- 使用封装的 framework, 由于可能不知道实现逻辑,也会出现 keyPath
removeObserver
两次的情况
使用场景
Block
Delegate
NSNotification