简介:KVO全称为Key Value Observing,键值监听机制,由NSKeyValueObserving协议提供支持,NSObject类继承了该协议,所以NSObject的子类都可使用该方法。
应用场景:当数据模型的数据发生改变时,视图组件能动态的更新,及时显示数据模型更新后的数据。
比如:监听scrollView的contentOffset属性,来完成用户滚动时动态改变某些控件的属性实现效果,包括渐变导航栏、下拉刷新控件等效果。
实现原理简要说明:KVO 是基于 runtime 机制实现的 当某个类的属性对象第一次被观察时,系统就会在运行期动态地创建该类的一 个派生类,在这个派生类中重写基类中任何被观察属性的 setter 方法。派生类在 被重写的 setter 方法内实现真正的通知机制。如果原类为 Person,那么生成的派生类名为 NSKVONotifying_Person 每个类对象中都有一个 isa 指针指向当前类,当一个类对象的第一次被观察,那么 系统会偷偷将 isa 指针指向动态生成的派生类,从而在给被监控属性赋值时执行的是派生类的 setter 方法。键值观察通知依赖于 NSObject 的两个方法willChangeValueForKey:didChangevlueForKey:;在一个被观察属性发生改变之前, willChangeValueForKey: 一定会被调用,这就 会记录旧的值。而当改变发生后,didChangeValueForKey: 会被调用,继而observeValueForKey:ofObject:change:context: 也会被调用。
补充:KVO 的这套实现机制中苹果还偷偷重写了 class 方法,让我们误认为还是使用的当前类,从而达到隐藏生成的派生类。
KVO实现步骤
1.注册观察者
/*
options: 有4个值,分别是:
NSKeyValueObservingOptionOld 把更改之前的值提供给处理方法
NSKeyValueObservingOptionNew 把更改之后的值提供给处理方法
NSKeyValueObservingOptionInitial 把初始化的值提供给处理方法,一旦注册,立马就会调用一次。通常它会带有新值,而不会带有旧值。
NSKeyValueObservingOptionPrior 分2次调用。在值改变之前和值改变之后。
*/
//注册一个监听器用于监听指定的key路径
[self.testModel addObserver:self forKeyPath:@"str" options:NSKeyValueObservingOptionNew context:nil];
2.实现回调方法
//当key路径对应的属性值发生改变时,监听器就会回调自身的监听方法,如下
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey,id> *)change
context:(void *)contex
}
3.移除观察者
//删除指定的key路径监听器
[self.testModel removeObserver:self forKeyPath:@"str"];
//删除指定的key路径监听器,只是多了context参数
[self.testModel removeObserver:self forKeyPath:@"str" context:nil];
KVO原理
1.验证执行对象
在图一种为ZQTextModel创建两个对象 model1和model2,并且坚挺model的str属性,在修改model1和model2的str值时设置断点,在图一中可以看出model1在由于设置了监听,它的isa的指向由ZQTextModel变为了NSKVONotifying_ZQTextModel
![](https://img.haomeiwen.com/i1706906/b93382f7f2df7518.png)
2.验证执行方法
在图二中先获取ZQTextModel设置KVO前后「setStr:」的IMP地址然后通过LLDB获取在可执行文件中的具体调用,可以看出在设置KVO后,调用方法变成了_NSSetObjectValueAndNotify
![](https://img.haomeiwen.com/i1706906/113777b18d34ae3e.png)
_NSSetObjectValueAndNotify
_NSSetObjectValueAndNotify 进行断句 _ NS SetObject
Value And Notify 其中 “Object”根据属性类型变换
猜测_NSSetObjectValueAndNotify的实现
void _NSSetObjectValueAndNotify(){
[self willChangValueForKey:@"str"]
[self setStr:@"str"]
[self didChangValueForKey:@"str"]
}
验证_NSSetObjectValueAndNotify的实现
在ZQTextModel中重写willChangeValueForKey:
、didChangeValueForKey:
方法
![](https://img.haomeiwen.com/i1706906/5031beb3c47dd432.png)
动态生成子类重写的方法
object_getClass 是获取的isa的指向
- (class)class;
- (void)dealloc;
- (BOOL)isKVOA;
主动出发kvo
直接调用 willChangValueForKey: 和 didChangValueForKey:
网友评论