官方文档点这里:NSKeyValueObserving、Key-Value Observing Programming Guide。
NSKeyValueObserving (KVO) 非正式协议定义了一个机制,允许一个对象在其他对象的属性发生变化时,收到通知。
<p>
Overview
<p>
可以观察任意的对象属性,包括 simple attributes, to-one relationships, 和 to-many relationships。attributes 可以理解为标量,比如 int,或者 NSNumber 对象。to-one relationships 可以理解为一个对象,比如一个学生类对象。to-many relationships 可以理解为集合对象,比如 NSArray 对象。观察 to-many relationships 的观察者可以被通知变化的类型和发生变化的对象。
NSObject 实现了 KVO,可以禁止自动通知和手动通知观察者。
<p>
Symbols
Change Notification
<p>
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey,id> *)change
context:(void *)context;
- 通知观察者发生了变化。
- keyPath,发生变化的属性。
- object,发生变化的对象,被观察者。
- change,包含变化的字典。可用的 key 有 newKey、oldKey、kindKey(比如 insert、replacement,具体看 NSKeyValueChange 部分) 等,分别获取属性的新值、旧值、变化类型(比如插入、替换)。详情请看 Change Dictionary Keys 部分。
- context,观察者注册 kvo 时提供的值。
- 调用 add Observer: for Key Path: options: context: 注册观察者,调用 remove Observer: for Key Path: or remove Observer: for Key Path: context: 移除观察者。当目标发生变化时,观察者就会调用本函数。需要注意的是,调用访问器(getter 和 setter)才会触发 kvo。
<p>
Registering for Observation
<p>
- (void)addObserver:(NSObject *)observer
forKeyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
context:(void *)context;
- 注册观察者,接收 kvo 通知。
- observer,观察者,必须实现 *observeValueForKeyPath: ofObject: change: context: *方法,收到通知时会调用。
- keyPath,发生变化的属性。不能为 nil。
- options,定义 kvo 通知要传递的内容,有 NSKeyValueObservingOptionNew、Old、Initial、Prior。比如 New 意味着通知的 change 字典包含变化之后的值,具体看 NSKeyValueObservingOptions 部分。
- context,随意的数据,传给观察者的 observeValueForKeyPath: ofObject: change: context: 方法。
- 不会 retain 观察者和被观察者(Neither the object receiving this message, nor observer, are retained.)。记得调用 remove Observer: for Key Path: 或 remove Observer: for Key Path: context: 移除观察者。
<p>
- (void)removeObserver:(NSObject *)observer
forKeyPath:(NSString *)keyPath;
- 移除对某个属性的观察。
- 如果移除的对象并没有注册为观察者,会引发异常。
- 在观察者被 deallocated 之前调用本函数,否则给释放掉的对象发送通知会引发异常。
<p>
- (void)removeObserver:(NSObject *)observer
forKeyPath:(NSString *)keyPath
context:(void *)context;
- 移除对某个属性的观察。
- 如果移除的对象并没有注册为观察者,会引发异常。
- 在观察者被 deallocated 之前调用本函数,否则给释放掉的对象发送通知会引发异常。
- 一个对象可能会对相同的 key path 注册多次,可以根据 context 判断要移除哪一次。
<p>
Notifying Observers of Changes
<p>
- (void)willChangeValueForKey:(NSString *)key;
- 属性将要发生变化时通知观察者。用于手动通知观察者。
- change type 是 NSKeyValueChangeSetting。
- 注意,修改属性值之后,要调用 didChangeValueForKey: 。
- 极少重写本函数,如果重写,记得调用 super 的函数。
<p>
- (void)didChangeValueForKey:(NSString *)key;
- 通知观察者,属性值已经发生变化。用于手动通知观察者。
- 极少重写本函数,如果重写,记得调用 super 的函数。
<p>
- (void)willChange:(NSKeyValueChange)changeKind
valuesAtIndexes:(NSIndexSet *)indexes
forKey:(NSString *)key;
- 通知观察者,一个 ordered to-many relationship(比如 NSArray 对象)的 indexes 位置的元素将要发生变化。用于手动通知观察者。
- change,变化类型,具体看 NSKeyValueChange 部分。
- indexes, 发生变化的元素下标集合。
- key,属性名,比如 students。
- 注意,属性值变化之后,要调用 didChange:valuesAtIndexes:forKey: 。
- 很少重写本函数,如果重写,记得调用 super 的函数。
<p>
- (void)didChange:(NSKeyValueChange)changeKind
valuesAtIndexes:(NSIndexSet *)indexes
forKey:(NSString *)key;
- 通知观察者位于 indexes 的元素发生了变化。用于手动通知观察者。
- change,变化类型,具体看 NSKeyValueChange 部分。
- indexes, 发生变化的元素下标集合。
- key,属性名,比如 students。
- 很少重写本函数,如果重写,记得调用 super 的函数。
<p>
- (void)willChangeValueForKey:(NSString *)key
withSetMutation:(NSKeyValueSetMutationKind)mutationKind
usingObjects:(NSSet *)objects;
- 通知观察者,一个 unordered to-many relationship(比如 NSSet 对象)将要发生什么类型的变化。用于手动通知。
- key,属性名,比如 studentSet。
- mutationKind,变化类型,比如交集、并集,具体看 NSKeyValueSetMutationKind 部分。
- objects,参与变化的对象,比如取交集需要两个集合对象。
- 当发生变化之后,记得调用 didChangeValueForKey:withSetMutation:usingObjects: 函数。
- 重写时记得调用父类方法。
<p>
- (void)didChangeValueForKey:(NSString *)key
withSetMutation:(NSKeyValueSetMutationKind)mutationKind
usingObjects:(NSSet *)objects;
- 通知观察者,一个 unordered to-many relationship(比如 NSSet 对象)发生了什么类型的变化。用于手动通知。
- key,属性名,比如 studentSet。
- mutationKind,变化类型,比如交集、并集,具体看 NSKeyValueSetMutationKind 部分。
- objects,参与变化的对象,比如取交集需要两个集合对象。
- 重写时记得调用父类方法。
<p>
Observing Customization
<p>
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key;
- 返回一个布尔值,表示是否支持自动 kvo。
- 返回 YES 表示自动调用 willChangeValueForKey:/didChangeValueForKey: 和 willChange:valuesAtIndexes:forKey:/didChange:valuesAtIndexes:forKey:,或者是遵从 kvc 的方法。
- 默认返回 YES。
<p>
+ (NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key;
- 返回 key path 的集合对象,其对应的属性值影响参数 key 对应的属性。比如姓名由姓和名决定。
- 默认实现是查找类似 +keyPathsForValuesAffecting<Key> 的方法,如果找到了就返回类似方法的结果集。可以重写该方法,但要调用 super 的方法。具体看 kvo 编程指导,文章顶部有链接。
<p>
@property void *observationInfo;
// demo。self 是控制器对象。
- (void)testObservationInfo {
UIViewController *vc = [UIViewController new];
[self addObserver:vc forKeyPath:@"view" options:NSKeyValueObservingOptionNew context:NULL];
id observationInfo = self.observationInfo;
}
- 一个指针,指向所有观察者的标识信息。
- The default implementation of this method retrieves the information from a global dictionary of observed objects keyed by memory addresses.
- For improved performance, both this property and observationInfo can be overridden to store the opaque data pointer in an instance variable. Overrides of this property must not attempt to send messages to the stored data.
- 可以参考这篇文章:http://www.bkjia.com/IOSjc/993206.html。
- 实际指向一个 NSKeyValueObservationInfo 对象,里面有个数组存放 NSKeyValueObservance 对象。这两个类在文档中并没有找到。NSKeyValueObservance 对象包含了观察者、观察的属性、依赖的属性、注册观察者时的 context 等。如下图所示,观察者是一个控制器对象,被观察的是 view 属性,context 是 NULL。
<p>
<p>
Constants
<p>
NSKeyValueChange
typedef NS_ENUM(NSUInteger, NSKeyValueChange) {
NSKeyValueChangeSetting = 1,
NSKeyValueChangeInsertion = 2,
NSKeyValueChangeRemoval = 3,
NSKeyValueChangeReplacement = 4,
};
- 表示变化类型。 用于函数 observeValueForKeyPath:ofObject:change:context: 的 change 字典的 NSKeyValueChangeKindKey 对应的值。
- setting 表示被设置了新值。
- insertion 表示一个 to-many relationship 插入了新的元素。
- removal 表示一个 to-many relationship 删除了元素。
- replacement 表示一个 to-many relationship 替换了元素。
<p>
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
};
- 用于函数 addObserver:forKeyPath:options:context: 的参数,决定函数 observeValueForKeyPath:ofObject:change:context: 的 change 字典包含哪些键值对。传 0 使得 change 字典不包含其他键值对,除了 NSKeyValueChangeKindKey。
- new 表示 change 字典包含 NSKeyValueChangeNewKey 键值对,可以获取新值。
- old 表示 change 字典包含 NSKeyValueChangeOldKey 键值对,可以获取旧值。
- initial 用于获取初始值,和 new 一起使用。在 addObserver:forKeyPath:options:context: 返回之前,就会发送一次通知。对于观察者来说是新值,所以和 new 一起使用,change 字典会包含 NSKeyValueChangeNewKey 键值对,可以获取初始值。
- prior 表示发送两次通知,在发生变化前后各一次。发生变化前发送的通知的 change 字典里面有个 NSKeyValueChangeNotificationIsPriorKey,对应的值是表示 YES 或者 NO 的 NSNumber 对象,但字典不会包含 NSKeyValueChangeNewKey。发生变化后发送的通知的 change 字典,和不使用 prior 是一样的,也就是没有 NSKeyValueChangeNotificationIsPriorKey 了(When this option is specified the change dictionary in a notification sent after a change contains the same entries that it would contain if this option were not specified )。用于手动通知的时候,有机会调用 -willChangeValueForKey: 方法,通知会跟着变化的属性的观察者(比如 firstName 要变化了,想手动通知 fullName 的观察者)。
<p>
Change Dictionary Keys
NSKeyValueChangeKey const NSKeyValueChangeKindKey;
NSKeyValueChangeKey const NSKeyValueChangeNewKey;
NSKeyValueChangeKey const NSKeyValueChangeOldKey;
NSKeyValueChangeKey const NSKeyValueChangeIndexesKey;
NSKeyValueChangeKey const NSKeyValueChangeNotificationIsPriorKey;
- 用于 observeValueForKeyPath:ofObject:change:context: 函数的change 字典。
- kindKey 表示变化类型,具体看 NSKeyValueChange 部分。
- newKey 表示变化后的值。注册观察者时用的 NSKeyValueObservingOptionNew,字典才会有这个键值对。如果 kindKey 是 NSKeyValueChangeInsertion 或者 replacement,newKey 对应的值是一个 NSArray 对象,包含插入或者被替换的元素。
- oldKey 表示变化前的值。注册观察者时用的 NSKeyValueObservingOptionOld,字典才会有这个键值对。如果 kindKey 是 NSKeyValueChangeRemoval 或者 replacement,newKey 对应的值是一个 NSArray 对象,包含删除或者被替换的元素。
- indexesKey 表示发生变化的元素下标,值是 NSIndexSet 对象,如果 kindKey 是 NSKeyValueChangeInsertion、replacement、removal 的话。
- NotificationIsPriorKey 表示是否变化前后发送通知,如果注册观察者时用的 NSKeyValueObservingOptionPrior。
<p>
NSKeyValueSetMutationKind
typedef NS_ENUM(NSUInteger, NSKeyValueSetMutationKind) {
NSKeyValueUnionSetMutation = 1,
NSKeyValueMinusSetMutation = 2,
NSKeyValueIntersectSetMutation = 3,
NSKeyValueSetSetMutation = 4
};
- 用于 willChangeValueForKey:withSetMutation:usingObjects: 和 didChangeValueForKey:withSetMutation:usingObjects: 函数的参数。含义与 NSMutableSet 的方法 unionSet:、minusSet:、intersectSet: 和 setSet: 相对应。
- Union 表示发生了并集。change 字典的NSKeyValueChangeKindKey 对应的值是 NSKeyValueChangeInsertion。
- Minus 表示两个集合相减,也就是减去两个集合中相同的元素。change 字典的NSKeyValueChangeKindKey 对应的值是 NSKeyValueChangeRemoval。
- Intersect 表示求交集,删除与另一个集合不相同的元素。change 字典的NSKeyValueChangeKindKey 对应的值是 NSKeyValueChangeRemoval。
- Set 表示集合的所有元素被另一个集合的元素代替。change 字典的NSKeyValueChangeKindKey 对应的值是 NSKeyValueChangeReplacement。
网友评论