美文网首页
KVO原理探秘

KVO原理探秘

作者: 天津的树懒 | 来源:发表于2020-02-23 15:28 被阅读0次

KVO的全称是Key-Value Observing,俗称“键值监听”,可以用于监听某个对象属性值的改变。

对于iOS开发者而言,相信大家都使用过KVO。但是对它的原理可能就不太清楚了。KVO是Runtime运行时机制的又一重大的应用,下面就对KVO的原理做一个详尽的阐述。

创建一个新的项目,然后在项目中创建一个ZPPerson类,这个类中有一个age属性。

ZPPerson类 age属性

然后在ViewController.m文件中创建一个person对象,并给它添加KVO监听。

创建person对象 给person对象添加KVO监听

当用户点击屏幕的时候,改变person对象的age属性的值。

改变age属性的值

这个时候系统就会调用监听方法了。

调用监听方法

下面阐述这个过程中KVO的实现原理:

  1. 在给这个person实例对象添加KVO监听之后,系统会利用Runtime机制动态地创建一个ZPPerson类的子类,名字就叫做"NSKVONotifying_ZPPerson"。然后系统会把这个person实例对象里面的isa指针由原来的指向ZPPerson类的class对象变为现在的指向NSKVONotifying_ZPPerson类的class对象;
  2. 新创建的子类"NSKVONotifying_ZPPerson"的class对象里面存储着isa指针、superclass指针、"setAge:"实例方法、"class"实例方法、"dealloc"实例方法以及"_isKVOA"实例方法等。其中系统会重写"setAge:"实例方法,重写后的该方法与它父类中的该方法的实现是不一样的,只不过方法的名称是一样的而已;
  3. 添加完KVO监听之后,当开发者调用"setAge:"实例方法来修改person对象的age属性的时候,根据上面所述,这个instance对象里面的isa指针已经由原来的指向ZPPerson类的class对象变为了现在的指向NSKVONotifying_ZPPerson类的class对象了,所以系统根据这个instance对象里面的isa指针找到的是NSKVONotifying_ZPPerson类的class对象,然后在这个class对象里面找到重写后的"setAge:"实例方法,最后再进行调用。这个重写的"setAge:"实例方法里面会调用Foundation框架里面的C语言函数"_NSSetIntValueAndNotify();",这个函数的实现里面首先会执行"[self willChangeValueForKey:@"age"];"代码,然后再执行"[super setAge:age];"代码,在执行这句代码的时候就会执行它的父类,也就是ZPPerson类里面的"setAge:"方法,从而真正更改这个属性的值,最后再执行"[self didChangeValueForKey:@"age"];"代码。"didChangeValueForKey:"这个方法里面会通知监听器"age"属性的值被改变了,即调用"- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context"监听方法,从而实现了对对象的某个属性进行监听的目的。

可以从下面的图中看出NSKVONotifying_ZPPerson类和ZPPerson类的关系:

NSKVONotifying_ZPPerson类和ZPPerson类的关系

除了上述的改变对象的属性值的时候会自动调用KVO的监听方法之外,想要在不改变对象属性值的时候也能自动调用KVO的监听方法,应该怎么做呢?

手动触发KVO:

在点击屏幕的时候调用手动触发KVO的方法:


调用手动触发KVO的方法

在方法中需要主动调用"willChangeValueForKey:"和 "didChangeValueForKey:"方法。

手动触发KVO方法

这样的话,在点击屏幕的时候就会手动触发KVO的监听方法了"- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context"。

image.png

以上就是对KVO原理的阐述。

Github Demo

补充:

修改对象里面的成员变量的值是不能够触发KVO的监听方法的,代码如下所示:

在ZPPerson类里添加一个_age成员变量:

_age成员变量

在ViewController.m文件中给age属性赋值:

给age属性赋值

点击屏幕的时候修改_age成员变量的值:

修改_age成员变量的值

运行之后可以知道通过这种方式改变对象的成员变量的值是无法自动触发KVO的监听方法的"- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context"。

修改对象的成员变量无法触发KVO监听方法的原因:

原因就在于对于已经被添加KVO监听的对象而言,instance对象的isa指针已经被改变为了指向系统新创建的子类的class对象了,然后在这个子类的class对象里面找到了(setAge:)实例方法,最后再进行调用。由之前的Demo可知,当调用新创建的子类的class对象中的属性的set实例方法的时候其实是在调用Foundation框架里面的C语言函数"_NSSetIntValueAndNotify",这个函数中会执行"didChangeValueForKey:"方法,此方法会触发KVO的监听方法。由此可知KVO的本质是调用set实例方法,即只有通过调用属性的set实例方法来修改属性的值才能触发KVO的监听方法,而修改对象的成员变量就没有调用属性的set方法,所以是不能够触发KVO的监听方法的。

如果想要在修改对象的成员变量的值的时候成功触发KVO的监听方法的话就要手动进行触发:

image.png

在手动触发方法的实现里面要调用"willChangeValueForKey:"和 "didChangeValueForKey:"方法:

image.png

运行之后就可以在控制台上看到成功调用了KVO的监听方法了:

控制台打印结果

Github Demo

”三人行,必有我师焉“, 欢迎各位批评指正。
如果您还觉得我写的不错的话请您点赞加关注,您的肯定是我前进的最大动力!
我是爱学习也爱您的树懒O(∩_∩)O

相关文章

  • KVO原理探秘

    KVO的全称是Key-Value Observing,俗称“键值监听”,可以用于监听某个对象属性值的改变。 对于i...

  • KVO的原理

    KVO基本原理: KVO深入原理: 适用于:

  • iOS KVO

    KVO 示例 KVO的实现原理

  • 知识集锦

    https://github.com/starainDou 欢迎点星 KVO实现原理 KVO基本原理: 1 kvo...

  • iOS原理篇(一): KVO实现原理

    KVO实现原理 什么是 KVO KVO 基本使用 KVO 的本质 总结 一 、 什么是KVO KVO(Key-Va...

  • iOS高级进阶之KVO

    KVO的原理 分析原理 使用 手动调用 自己实现KVO NSObject+KVOBlock.h NSObject+...

  • 常见面试题--KVC和KVO

    1、KVO实现原理 2、KVC原理

  • 04. KVO使用,原理,本质

    问题 KVO日常使用 KVO原理(KVO本质是什么) 如何手动触发KVO 直接修改成员变量会触发KVO吗 KVO图...

  • iOS-自定义KVO

    KVO原理及使用 我们之前讨论过KVO的原理,知道KVO机制是生成了一个中间类NSKVONotifying,该中间...

  • 20.iOS底层学习之KVO 原理

    本篇提纲1、KVO简介;2、KVO的使用;3、KVO的一些细节;4、KVO的底层原理; KVO简介 KVO全称Ke...

网友评论

      本文标题:KVO原理探秘

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