iOS KVO
KVO
全称为Key Value Observing,键值监听机制,由NSKeyValueObserving协议提供支持,NSObject类继承了该协议,所以NSObject的子类都可使用该方法。
一、KVO的主要应用场景
应用场景:当数据模型的数据发生改变时,视图组件能动态的更新,及时显示数据模型更新后的数据。
比如:监听scrollView的contentOffset属性,来完成用户滚动时动态改变某些控件的属性实现效果,包括渐变导航栏、下拉刷新控件等效果。
二、KVO实现步骤
1.注册观察者(为被观察这指定观察者以及被观察者属性)
/*
options: 有4个值,分别是:
NSKeyValueObservingOptionOld 把更改之前的值提供给处理方法
NSKeyValueObservingOptionNew 把更改之后的值提供给处理方法
NSKeyValueObservingOptionInitial 把初始化的值提供给处理方法,一旦注册,立马就会调用一次。通常它会带有新值,而不会带有旧值。
NSKeyValueObservingOptionPrior 分2次调用。在值改变之前和值改变之后。
*///注册一个监听器用于监听指定的key路径
[self.person addObserver:selfforKeyPath:@"name"options:NSKeyValueObservingOptionNew context:nil];
2.实现回调方法
//当key路径对应的属性值发生改变时,监听器就会回调自身的监听方法,如下
-(void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id>*)change context:(void*)contex{}
3.触发回调方法
3.1>触发kvo可以通过kvc的方法赋值,还可以调用setter方法
3.2>手动触发kvo,willChangeValueForKey:和DidChangeValueForKey:触发
4.移除观察者
//删除指定的key路径监听器
[self.person removeObserver:selfforKeyPath:@"name"];
//删除指定的key路径监听器,只是多了context参数
[self.person removeObserver:selfforKeyPath:@"name"context:nil];
三、KVO的实现原理简要说明
KVO 是基于 runtime 机制实现的 当某个类的属性对象第一次被观察时,系统就会在运行时动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的 setter 方法。派生类在被重写的 setter 方法内实现真正的通知机制。
如果原类为 Person,那么生成的派生类名为 NSKVONotifying_Person 每个类对象中都有一个 isa 指针指向当前类,当一个类对象的第一次被观察,那么 系统会偷偷将 isa 指针指向动态生成的派生类,从而在给被监控属性赋值时执行的是派生类的 setter 方法。
键值观察通知依赖于 NSObject 的两个方法willChangeValueForKey:didChangevlueForKey:;在一个被观察属性发生改变之前, willChangeValueForKey: 一定会被调用,这就 会记录旧的值。而当改变发生后,didChangeValueForKey: 会被调用,继而observeValueForKey:ofObject:change:context: 也会被调用。
补充:KVO 的这套实现机制中苹果还偷偷重写了 class 方法,让我们误认为还是使用的当前类,从而达到隐藏生成的派生类。