美文网首页iOS知识小集
KVO与KVC的实现原理

KVO与KVC的实现原理

作者: 王的for | 来源:发表于2018-09-04 14:28 被阅读0次

    一、KVO的实现原理

    KVO的全称是Key-ValueObserving(键值监听),可以用于监听某个对象属性值的改变。

    1、KVO的使用API如下

    通过addObserver: forKeyPath: options: context:添加观察者对某个属性的监听。

    2、大家看下我写的代码,在设置age打个断点来调试。

    为什么会这样?明明我的person实例是HPPerson类实例化而来的切发现他的类对象不再是HPPerson了,变成了NSKVONotifying_HPPerson。person1的类对象还是HPPerson。

    注意:不能用[self.person class]来获取解析本质。[self.person class],[self.person1 class]得到的结果都是HPPerson类,这个可能是苹果API不想你知道太多内层的实现哈哈。所以要利用isa指针的本质来看问题。

    说明:在使用[self.person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];时,在内部实现中通过Runtime动态生成了一个NSKVONotifying_HPPerson类。

    3、NSKVONotifying_HPPerson类是什么类?

    先看下setAge的实现。

    可以看出,由于person加了KVO监听,所以setAge的函数地址变了,实现也变了!setAge里面的具体实现是调用了_NSSetIntValueAndNotify()这个函数。但是我们从self.person.age=10;的设置结果可以看出,self.person.age设值后的age确实是10;也就是说这个成员变量_age是正确按照setAge:里面的实现(_age=age)做了赋值操作。而这个赋值操作时在Person类对象的方法列表中的setAge方法里面。说明_NSSetIntValueAndNotify()函数应该实现了setAge的方法实现。也就是要达到这种目的,只有一种情况,NSKVONotifying_HPPerson是HPPerson的子类。也就是在addObserver:时候Runtime动态创建的NSKVONotifying_HPPerson类是HPPerson类的子类。

    可以通过LLDB打印他的superclass指针来证明NSKVONotifying_HPPerson确实是HPPerson子类。

    由此可以看出添加KVO后的实例对象的类对象变成了NSKVONotifying_HPPerson类,而没有添加KVO的实例对象的类对象还是之前的HPPerson类。

    总结关系如下图:

    1)添加KVO的类关系图谱

    2)没有添加KVO的类关系图谱

    3)如果增加一个stirng类型的name属性,则会调用_NSSetCharValueAndNotify(),由此可以知道_NSSet**ValueAndNotify()有非常多个这个函数,根据不同类型属性调用不同函数名的函数,这个属于Foundation框架下的函数,可以通过越狱设备拿到Foundation的二进制文件,然后反编译后去查找这类函数的实现。过程太复杂,这里不深入写下去了。 在这里只要知道我们的KVO实质是利用了Runtime生成了一个子类,在子类的setAge方法中调用_NSSetIntValueAndNotify()来实现的。

    4)_NSSetIntValueAndNotify()这系列函数内部实现逻辑大概是如下这样:

    通过willChangeValueForKey与didChangeValueForKey的调用,可以实现手动触发KVO监听。

    所以可以大概知道这个函数内部实现应该是如下:

    在didChangeValueForKey:里面实现了内部调用属性观察者observer的observeValueForKeyPath:ofObject:change:context:方法。

    4、通过KVC设值age会不会触发KVO监听吗?

    结果可以发现,KVC也会触发KVO监听。其内部也是实现了willChangeValueForKey与didChangeValueForKey的调用。

    二、KVC设值取值原理流程图

    常用API使用:

    - (void)setValue:(id)value forKeyPath:(NSString *)keyPath;

    - (void)setValue:(id)value forKey:(NSString *)key;

    - (id)valueForKeyPath:(NSString *)keyPath;

    - (id)valueForKey:(NSString *)key;

    1、设值原理

    说明:+(BOOL)accessInstanceVariablesDirectly方法默认返回值YES;

    2)取值原理

    对于KVC的设值取值的顺序,可以自己建一个类把对应方法写进去看下执行及顺序。

    三、总结

    0、利用addObserver:forKeyPath...函数增加观察值变化,内部实现会利用Runtime动态生成一个NSKVONotifying_HPPerson类(HPPerson的子类)。NSKVONotifying_HPPerson类中也有setAge方法,实现代码是直接调用了Foundation下的_NSSet***ValueAndNotify()系里函数。

    1、_NSSet***ValueAndNotify()系里函数实现逻辑为调用willChangeValueForKey与didChangeValueForKey方法来实现。

    2、-(void)didChangeValueForKey方法内部实现[observer observeValueForKeyPath: ofObject...]的调用。

    3、KVO需要通过set方法来实现键值监听,如果直接赋值成员变量是不会触发KVO。

    4、KVO可以通过手动调用-(void)willChangeValueForKey与-(void)didChangeValueForKey方法来实现。

    5、KVC设值会触发KVO。

    相关文章

      网友评论

        本文标题:KVO与KVC的实现原理

        本文链接:https://www.haomeiwen.com/subject/hiriwftx.html