本文简述KVO的底层实现是怎么一回事
如何使用KVO
举例
1 | self.person = [[Person alloc] init]; |
1 | /// 当监听对象的属性值发生改变时,就会调用这个方法 |
内部发生了什么
系统通过运行时机制,给person
对象添加了一个父类,名为NSKVONotifying_Person
(其他被监听的对象所生成的类也会遵循这个命名规则),本质上是将person
对象的isa指针指向了这个动态新建的类对象。
另外,这个新建的类对象是继承自Person
的,也就是NSKVONotifying_Person
的superclass指针指向了Persond
的类对象。
NSKVONotifying_*类内部结构
- isa指针
- superclass指针
- setAge:方法 因为监听的是age属性,所以重写了setAge方法,若监听的属性没有set方法,或者值改变的时候没有调用set方法,那么KVO无法生效
- class:方法,主要是为了防止对象调用class方法的时候得到了这个动态生成的类对象,所以重写class方法,返回真正
的Person
类对象 - _isKVOA方法,返回一个bool,用来判断这是不是一个KVO类
- dealloc 进行一些收尾工作
重写的set方法的实现
大概是这么实现的
1 | - (void)setAge:(int)age { |
这里的方法_NSSetIntValueAnyNotify
,看具体监听的属性类型而定,如果监听的是double
类型,那么方法就会变成_NSSetDoubleValueAnyNotify
,所以系统内置了很多这种类似的方法,统称为_NSSet*ValueAnyNotify
调用流程
- willChangeValueForKey:
- setAge:
- observeValueForKeyPath:ofObject:change:context:
- didChangeValueForKey:
总结
利用Runtime API动态生成一个之类,并且让instance对象的isa指针指向这个全新的子类
当修改instance对象的属性时,会调用Foundation的_NSSetXXXValueAnyNotify函数
- willChangeValueForKey:
- 父类原来的setter
- didChangeValueForKey:
- 内部会触发监听器(Observer)的监听方法(observeValueForKeyPath:ofObject:change:context:)
如何手动触发KVO
注册监听后,手动调用willChangeValueForKey:
和didChangeValueForKey:
直接修改成员属性的值不会触发KVO,必须在前后分别补上
willChangeValueForKey:
和didChangeValueForKey: