探索KVO的本质(二)
通过探索kvo的本质(一)我们已经了解到很多关于kvo的基础知识,接下来我们看下面的代码:
我们知道:通过[** class]和object_getClass(**) 都能获取实例对象的类对象,而从上面的运行结果,我们很容易知道,通过runtime的object_getClass(**)获取的就是它的真实的类对象,而[** class]获取的是它的什么?
还记得上一个博客的图吗?
这里直接说结论,是因为[self.person1 class],class方法由于看不到源码,但是我们可以猜测它的大概实现如下:
-(Class)class
{ return [GDPerson class] } 里面可能是这个实现,猜测大概是这样
其实这样也是不给开发者一个疑惑,也是屏蔽NSKVONotifying_GDPerson的内部实现,隐藏NSKVONotifying_GDPerson这个类,也是重写了这个方法,让[self.person1 class]返回的结果变成了GDPerson.
其实你可以查看[** class]这个方法(commad+jump)最终查找的就是NSObject,你这个对象是什么类型,返回的最终结果就是什么类型.
验证NSKVONotifying_GDPerson里面方法
有同学可能疑惑,你怎么知道NSKVONotifying_GDPerson里面会有setAge:,class;dealloc;isKVOA这些方法的存在呢?(也就是上面那张图绿色的部分),接下来我就证明这些方法的存在
证明这个需要用到运行时的一个函数:class_copyMethodList(<#Class _Nullable __unsafe_unretained cls#>, <#unsigned int * _Nullable outCount#>),这个函数的描述就是获取类的方法,第一个参数就是类名,第二个涉及c语言的知识,这里就不直接过多描述,直接贴代码:
这里是不是瞬间感觉6666!😄,调用的时候我们需要传入真正的类对象也就是
[self printMethodNamesofClass:object_getClass(self.person1)];
这就证明了我们上面的疑问
总结结论:
1.iOS用什么方式实现对一个对象的KVO?(KVO的本质是什么?)
a.利用RuntimeAPI动态生成一个子类,并且让instance对象的isa指向这个全新的类
b.当修改instance对象的属性时,会调用Foundation的_NSSetXXXValueAndNotify函数
c.WillChangeValueForKey;父类原来的setter方法;didChangeValueForKey
d.内部会触发监听器(Oberser)的监听方法 oberserValueForKeyPath:ofObject:change:context
2.如何手动触发KVO?
手动调用willChangeValueForKey和didChangeValueForKey就行了
(这里大家可以尝试验证,只要执行didChangeValueForKey就会调用,因为执行did内部会验证will,所以要一起写, [self.person1 willChangeValueForKey:@"age"] [self.person1 didChangeValueForKey:@"age"];)
3.直接修改成员变量会不会触发KVO
不会触发kvo (只有执行了属性的set方法才会触发或者手动触动)
(这个不验证了,很简单,大家自己尝试)