一、前言
Objective-C 中的键(key)-值(value)观察(KVO)并不是什么新鲜事物,它来源于设计模式中的观察者模式,其基本思想就是:
一个目标对象管理所有依赖于它的观察者对象,并在它自身的状态改变时主动通知观察者对象。这个主动通知通常是通过调用各观察者对象所提供的接口方法来实现的。观察者模式较完美地将目标对象与观察者对象解耦。
在 Objective-C 中有两种使用键值观察的方式:手动或自动,此外还支持注册依赖键(即一个键依赖于其他键,其他键的变化也会作用到该键)。下面将一一讲述这些,并会深入 Objective-C 内部一窥键值观察是如何实现的。
二、KVO机制
1、注册与解除注册
如果已经有了包含可供KVO属性的类(即被观察类),那么就可以通过在该类对象(被观察类对象)上调用名为 NSKeyValueObserverRegistration 的 category 方法将观察者对象与被观察者对象进行注册与解除注册:
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context;
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(void *)context NS_AVAILABLE(10_7, 5_0);
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;
这个三个方法的定义在这两个方法的定义在 Foundation/NSKeyValueObserving.h 中,NSObject,NSArray,NSSet均实现了以上方法,因此我们不仅可以观察普通对象,还可以观察数组或集合类对象。
提示:不要忘记解除注册,否则会导致资源泄露。
2、处理变更通知
当被观察者类对象中某属性发生变更,观察者需要处理接收到的变更通知。在观察者类中,需要实现名为 NSKeyValueObserving 的 category 方法:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;
其中:change 这个字典保存了哪些变更信息,取决于注册时的 NSKeyValueObservingOptions
typedef NS_OPTIONS(NSUInteger, NSKeyValueObservingOptions) {
NSKeyValueObservingOptionNew = 0x01,
NSKeyValueObservingOptionOld = 0x02,
NSKeyValueObservingOptionInitial NS_ENUM_AVAILABLE(10_5, 2_0) = 0x04,
NSKeyValueObservingOptionPrior NS_ENUM_AVAILABLE(10_5, 2_0) = 0x08
};
3、手动或者自动实现KVO通知
KVO机制提供两种变更消息通知模式:手动实现,自动实现。
在 NSKeyValueObservingCustomization 的 category 中有方法:
/* Return YES if the key-value observing machinery should automatically invoke -willChangeValueForKey:/-didChangeValueForKey:, -willChange:valuesAtIndexes:forKey:/-didChange:valuesAtIndexes:forKey:, or -willChangeValueForKey:withSetMutation:usingObjects:/-didChangeValueForKey:withSetMutation:usingObjects: whenever instances