1.KVO的实现原理
当你调用addObserver去观察一个实例对象时,runtime会动态创建一个叫做NSKVONotifying_<原类名>的新类,它继承自该实例对象的原本的类,并重写了新类中被观察属性的setter方法。重写的setter方法会负责在调用super的setter方法之前和之后,(所以,setter方法的调用并不是通过hook,而是通过继承)通知所有观察对象值的更改。具体set方法如何重写,看下边的2。
在这里边有个细节,在添加观察者,动态创建NSKVONotifying_<原类名>之后,会将原类的isa指针指向这个动态创建的新类,所以当你调用原类的set方法时实际上调用的是NSKVONotifying_<原类名>的set方法。然后NSKVONotifying_<原类名>里的class方法,返回的是原类的类名,这是苹果并不希望把NSKVONotifying_<原类名>这个类暴露出来,屏蔽内部实现,隐藏这个类的存在。
其实简单的思路其实就是在set方法前后分别加上willChangeValueForKey:和didChangeValueForKey:这两个方法,而对变化做出处理的observeValueForKeyPath:方法的调用实际上也就是在方法didChangeValueForKey:中被调用的。
手动触发valueChange 还需要重写一个automaticallyNotifiesObserversForKey方法,将需要检测的属性的自动监测判断改为NO
那么“手动触发”的使用场景是什么?一般我们只在希望能控制“回调的调用时机”时才会这么做。
或者属性被声明为readonly。那么在子类中通过@synthesize重写该变量的时候,对其进行赋值的时候就需要手动触发,因为readonly不可以调用set方法
返回NO就说明age的监测 由手动控制只有成对调用willchage 和 didChange 方法时 才会 触发KVO。 如果不重写这个方法,那么通过set赋值 和 willChange/didChange 方法都会触发KVO
2.说说KVO怎么重写的set方法
首先获取该属性的旧值。然后给动态创建的NSKVONotifying_<原类名>这个类的父类,也就是我们能看到的被观察的对象,发送set新值的消息。最后给所有观察者发送消息,告诉其属性值已经改变。也就是调用observeValueForKeyPath:方法。
如何对数组实现KVO
举个例子,myItems 是我们要进行 KVO 的一个属性,它的定义如下:
@property (nonatomic, strong) NSMutableArray *myItems;
在它进行添加元素时,使用如下方法:
[[self mutableArrayValueForKey:@"myItems"] addObject:item];
这样,我们便实现了对 NSMutableArray 的 KVO。
何时触发
1. 直接调用set方法,或者通过属性的点语法间接调用
2.使用KVC的setValue:forKey:方法
3. 使用KVC的setValue:forKeyPath:方法
4.通过mutableArrayValueForKey:方法获取到代理对象,并使用代理对象进行操作
参考文章:
https://www.jianshu.com/p/7ff5ffb31ead
网友评论