iOS | KVO | Objective-C
KVO的本质是什么,如何手动触发KVO?
1.什么是KVO
KVO的全称是Key-Value Observing,俗称“键值监听”,可以用于监听某个对象属性值的改变
添加监听:
1 | typedef enum NSKeyValueObservingOptions : NSUInteger { |
监听回调:
1 | - (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSString*, id> *)change context:(nullable void *)context; |
移除监听:
1 | - (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(nullable void *)context; |
KVO使用大家都比较熟悉,Demo应该就没有写的必要了,下面我们直接来探索下本质
2.KVO的本质
1 | - (void)viewDidLoad { |
通过断点调试,我们发现 person2 的类对象没有发生变化,person1 的类对象变成了 NSKVONotifying_Person,而且是Person的子类。
使用 Runtime 打印 NSKVONotifying_Person 方法列表:
1 | - (void)printMethodNamesOfClass:(Class)cls { |
- NSKVONotifying_Person 实例方法列表:
- setAge:
- class
- dealloc
- _isKVOA
总结:
1> 添加监听时,利用RuntimeAPI动态生成Person子类: NSKVONotifying_Person ,并且使 person1 的 isa 指针指向新的类
2> 重写 setAge: ,person1 调用 setter 方法时会从 NSKVONotifying_Person 开始查找,在自己的类对象中能够找到,所以会调用自己的 setAge:方法( 会调用Foundation的_NSSetValueAndNotify函数)
3> _NSSetValueAndNotify 调用流程:willChangeValueForKey -> [super setAge:] (Person 的 setter 方法) -> didChangeValueForKey(同时触发 observeValueForKeyPath 监听回调方法,订阅者接收)
4> class 方法:重写 class 方法的目的是什么呢?(lldb) po self.person1.class 输出为:Person,原来,苹果粑粑是想要隐藏NSKVONotifying_Person,让开发者无感,使用时与未添加监听时无异
5> dealloc方法:释放 KVO 新产生的资源
6> _isKVOA方法:标记这个新类 KVO 机制新建的
对willChangeValueForKey/didChangeValueForKey还有疑惑的同学可以在 Person.m 中对这两个方法进行重写,再进行调试以变理解。