iOS KVO的实现原理

KVO是什么

  • KVO是OC的一种观察者模式,另一种观察者模式是通知机制(notification)
  • KVO的机制:指定一个被观察对象,当该对象某个属性发生变化时,观察对象会获得通知,并作出相应动作,并且被观察的对象不需要添加任何额外的代码

在MVC设计架构下,KVO机制通常实现数据模型和视图之间的通讯,这样可以保证数据和视图显示达到同步。

实现原理

KVO 的实现依赖于OC强大的运行时 Runtime

原理:

当观察某对象时,KVO动态创建该对象的子类,将原始类和子类的属性设置setter 方法进行交互,并重写子类被观察属性 setter 方法,随后通知观察者该属性的变化状况。

因此运行时主要做了三件事。

  1. 创建子类
  2. 方法交换
  3. 通知观察者

运行时在 KVO 机制中扮演了黑客的身份,截获了用户的信息,稍加改造之后继续发出去,但同时将用户的信息解析之后贩卖给了别人(监听者)。

实现过程:

Apple使用方法交换(isa-swizzling)来实现KVO。

当观察对象A时,KVO动态创建了新的名为 NSKVONotifying_A 的新类,该类是对象A的子类,并且使用 swizzling 交换了所观察的属性的 setter 方法,KVO重写了新类的观察属性的 setter 方法,在调用原类中的 setter 方法后,通知所有观察者该属性的变化情况。

  • NSKVONotifying_A

每个对象内部都有 isa 指针,这个指针指向该对象的类,在KVO机制中,该isa指针被修改为指向系统新创建的子类 NSKVONotifying_A ,那么当被观察者修改被检测的属性的值时候,就会调用KVO重写的setter方法,从而激活键值通知机制,实现当前类属性改变的监听。

所以当我们从应用层面上来看,并没有意识到有新类的出现,这是apple隐瞒类对KVO的底层实现过程,而我们还以为是原来的类,但是此时如果我们创建一个新的名为 NSKVONotifying_A 的类时,就会发现系统运行到注册KVO的那段代码时,程序发生崩溃,因为系统在注册监听的时候动态创建了名为NSKVONotifying_A的中间类,并指向这个中间类了。

  • 子类重写 setter 方法

KVO的键值观察通知依赖于NSObject的两个方法:-willChangeValueForKey:didChangeValueForKey: ,在存取数值的前后分别调用2个方法;
被观察属性发生改变之前,-willChangeValueForKey: 被调用,通知系统该keyPath的属性值即将变更;当改变发生后,-didChangeValueForKey: 被调用,通知系统keyPath的属性值已经发生改变,之后,observeValueForKey:ofObject:change:context: 也会被调用。
注意:重写观察属性的setter方法这种继承方式的注入是在运行时而不是编译时实现的

注意

观察者观察的是属性,只有遵循 KVO 变更属性值的方式才会执行 KVO 的回调方法,例如执行 setter 方法、KVC方式赋值等,如果赋值没有通过KVO来变更属性值,而是直接修改属性对应的成员变量,例如:仅调用 _name=@"newName",这时是不会触发KVO机制的,更加不会调用回调方法的,因此,使用KVO机制的前提是遵循KVO属性设置方式来变更属性值。

拓展

1、和KVC的比较?

  • KVC,即Key-Value-Coding,是一个非正式协议,使用字符串(key)来访问一个对象实例变量的机制
  • KVO,即Key-Value-Observing,它提供一种机制,当被观察者的属性值更改时,观察者就会接收到通知

2、和通知的区别?

  • 相比于KVO重写setter时调用 observeValueForKey:ofObject:change:context: 方法,通知要多发送通知操作,例如重写类的setter方法,使用通知也可以实现类似KVO的监听
  • 两者都是一对多,通知监听不局限于属性的变化,还可以是状态的变化,监听范围广,例如键盘的出现、app进入后台等,使用也更灵活方便,但是通知的开销要大。

3、和delegate的不同?

KVO、通知、delegate三者都可以实现类之间的通信,但是delegate不同的是:

  • KVO和通知都负责发送和接收通知,剩下的事情都由系统来完成,所以不用返回值,而delegate则需要协议和代理对象来关联
  • delegate适用于一对一,KVO和通知则适用于一对多情况

4、涉及的技术

KVC和KVO实现的根本是OC语言的动态性和运行时runtime,以及访问器方法的实现。

总结

相比如其他消息回调的方式,KVO机制的实现更多的依赖于系统支持,它能够提供被观察属性的 newValueoldValue;但是由于需要创建对应的子类、重写setter方法等,内存消耗也是很大的,所以对于两个类之间的消息通信,我们应该根据实际应用的场景来选择通信方式。

另外需要注意的是,由于这中继承方式的注入是在运行时而非编译时实现,所以当没有观察者时,KVO不会有任何开销,此时也根本就没有KVO代码存在,但是如果时委托和通知那还是需要开销,这也是KVO零开销观察的优势。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值