22.KVO底层原理
一、 中间类
一般提到 KVO
底层,大家可能都知道是生成类一个 NSKVONotifying_XXX
的类。但是再往深了问可能就答不出来了。
1.1 中间类是动态生成的还是编译生成的?
在设置观察者之前我们通过调用 objc_getClass("NSKVONotifying_RYModel")
,看能否获取到 NSKVONotifying_RYModel
类。
添加观察者之后
***结论:***可见 NSKVONotifying_RYModel
确实是动态生成的,而不是编译完成后就已经存在的。
1.2 中间类 和 本类 的关系
先猜测 NSKVONotifying_RYModel
是否是 RYModel
的子类,来验证一下。
打印自己与所有子类
发现添加观察者后输出的多了一个 NSKVONotifying_RYModel
证明了它确实是 RYModel
的子类
***结论:***KVO动态创建的类是本类的一个子类。
1.3 中间类的生命周期是怎样的?是否会移除?
***结论:***移除观察者,观察者释放后并不会 NSKVONotifying_XXX
页依旧存在。
***思考:***由于中间类一旦生成就一直存在,所以理论上也不宜过多使用 KVO
。
1.4 中间类的方法列表和本类有什么区别?
添加打印方法的方法:
RYModel
SEL: name IMP: 0x100003210
SEL: .cxx_destruct IMP: 0x1000032a0
SEL: setName: IMP: 0x100003170
SEL: books IMP: 0x100003240
SEL: setBooks: IMP: 0x100003260
NSKVONotifying_RYModel
SEL: setBooks: IMP: 0x7fff212b9d2e
SEL: setName: IMP: 0x7fff212b9d2e
SEL: class IMP: 0x7fff2127cc06
SEL: dealloc IMP: 0x7fff212bf9f5
SEL: _isKVOA IMP: 0x7fff213e3e3a
1.5 中间类的方法是继承的还是重写的?
新建一个子类,重写父类的方法:
输出:
1.6 KVO 开关对方法列表的影响
停止 name 自动发送通知后,中间类的方法列表中就没有类 setName
。
二、 ISA 指向变换
上面我们知道被观察的对象类型变成类一个中间类。那么它是怎么变的呢?
2.1 改变
添加观察者之前
添加观察者之后
很明显实例的 ISA 的指向发生了变化。
2.2 恢复
开始移除观察者:
移除最后一个观察者:
观察者全部移除:
恢复了 RYModel
思考:全部Key移除了观察者才ISA恢复的本类,如果不完全移除呢?
剩了一个 Key 没有移除观察者
到最后实例的 ISA 指向还是 中间类。
三、 触发通知的过程
我们在 KVO 通知接收的地方下个断点,调用堆栈:
tool.ryuk.name = @"Ryukie";
Foundation`_NSSetObjectValueAndNotify
Foundation`-[NSObject(NSKeyValueObservingPrivate) _changeValueForKey🔑key:usingBlock:]
Foundation`-[NSObject(NSKeyValueObservingPrivate) _changeValueForKeys:count:maybeOldValuesDict:maybeNewValuesDict:usingBlock:]
Foundation`NSKeyValueDidChange
Foundation`NSKeyValueNotifyObserver
-[KVOTool observeValueForKeyPath:ofObject:change:context:]
总结
到此,我们知道了
使用 KVO 过程中动态生成了中间类
中间类和本类是继承关系,同时重写了本类的一些方法
自动监听开关对与中间类的结构是有影响的
被观察者 ISA 的指向会发生变化,完全移除所有观察Key后会恢复。
Last updated