KVO:Key-Value Observing,是Foundation框架提供的一种机制,使用KVO,可以方便地对指定对象的某个属性进行观察,当属性发生变化时,进行通知。
使用KVO只需要两个步骤:
- 注册Observer;
- 接收通知。
注册Observer
使用addObserver:forKeyPath:options:context:
消息注册Observer,其中三个参数的含义如下:
- observer:观察者,需要响应属性变化的对象。该对象必须实现
observeValueForKeyPath:ofObject:change:context:
方法。 - keyPath:要观察的属性名称。要和属性声明的名称一致。
- options:对KVO机制进行配置,修改KVO通知的时机以及通知的内容,在后面详解。
- context:接收一个C指针,指向希望监听的属性。如:
&self->_testData
需要注意的是,注册Observer之后一定要在合适的机会解除注册,否则会引发资源泄露,取消注册的方法:
1 | removeObserver:forKeyPath:context: |
参数含义同注册时方法的参数含义。
接收通知
当属性的值发生变化时,框架默认会自动通知注册的观察者。
上文提到,观察者需要实现方法:
1 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context |
这个方法就是接收通知的方法。参数含义如下:
- object:这个是所监听的对象,也就是所监听的属性所属的对象。
- change:是传入的变化量,通过在注册时用options参数进行的配置,会包含不同的内容。
- 其他参数含义同注册时方法的参数含义。
在实现这个方法中需要注意的是, 一定要对注册监听的所有属性都进行处理——使用context参数进行判断——否则Xcode会警告。
options参数
enum类型,在注册时传入,共有四种取值方式:
1 | enum { |
四个值的含义如下:
- NSKeyValueObservingOptionNew:接收方法中使用change参数传入变化后的新值,键为:NSKeyValueChangeNewKey;
- NSKeyValueObservingOptionOld:接收方法中使用change参数传入变化前的旧值,键为:NSKeyValueChangeOldKey;
- NSKeyValueObservingOptionInitial:注册之后立刻调用接收方法,如果配置了NSKeyValueObservingOptionNew,change参数内容会包含新值,键为:NSKeyValueChangeNewKey;
- NSKeyValueObservingOptionPrior:如果加入这个参数,接收方法会在变化前后分别调用一次,共两次,变化前的通知change参数包含notificationIsPrior = 1。其他内容根据NSKeyValueObservingOptionNew和NSKeyValueObservingOptionOld的配置确定。
change参数
除了根据options参数控制的change参数内容,默认change参数会包含一个NSKeyValueChangeKindKey键值对,传递被监听属性的变化类型:
enum {
NSKeyValueChangeSetting = 1,
NSKeyValueChangeInsertion = 2,
NSKeyValueChangeRemoval = 3,
NSKeyValueChangeReplacement = 4
};
typedef NSUInteger NSKeyValueChange;
含义很直白:
- NSKeyValueChangeSetting:属性的值被重新设置;
- NSKeyValueChangeInsertion、NSKeyValueChangeRemoval、NSKeyValueChangeReplacement:表示更改的是集合属性,分别代表插入、删除、替换操作。
如果NSKeyValueChangeKindKey参数是针对集合属性的三个之一,change参数还会包含一个NSKeyValueChangeIndexesKey键值对,表示变化的index。
自动通知和手动通知
上文提到,KVO默认会自动通知观察者。取消自动通知的方法是实现
1 | + (BOOL) automaticallyNotifiesObserversForKey:(NSString *)key |
方法,通过返回NO来控制取消自动通知。
针对非自动通知的属性,可以分别在变化之前和之后手动调用如下方法(will在前,did在后)来手动通知观察者:
- (will/did)ChangeValueForKey:
- (will/did)ChangeValueForKey:withSetMutation:usingObjects:
- (will/did)Change:valuesAtIndexes:forKey:
因为大多情况下都使用自动通知,这里就不过多解释。事实上自动通知也是框架通过调用这些方法实现的。
Demo
下面这个例子演示了KVO的使用,供大家参考。运行效果:
Code:
1 |
|