Two techniques Ensuring the Changing Notification Emitted
There are two techniques for ensuring the change notifications are emitted.
Automatic Change Notification
NSObject provides a basic implementation of automatic key-value change notification. Automatic key-value change notification informs observers of changes made using key-value compliant accessors, as well as the key-value coding methods. Automatic notification is also supported by the collection proxy objects returned by, for example, mutableArrayValueForKey:
NSObject 提供了当属性值发生改变时自动发送通知的机制。
那什么时候自动发送通知呢?
- 使用遵守KVC的属性访问器
- 使用 KVC 方法,如 setValue:forKey: or setValue:forKeyPath:
- mutableArrayValueForKey: 返回的代理集合对象也支持自动发送通知。
Examples of method calls that cause KVO change notifications to be emitted
// Call the accessor method.
[account setName:@"Savings"];
// Use setValue:forKey:.
[account setValue:@"Savings" forKey:@"name"];
// Use a key path, where 'account' is a kvc-compliant property of 'document'.
[document setValue:@"Savings" forKeyPath:@"account.name"];
// Use mutableArrayValueForKey: to retrieve a relationship proxy object.
Transaction *newTransaction = <#Create a new transaction for the account#>;
NSMutableArray *transactions = [account mutableArrayValueForKey:@"transactions"];
[transactions addObject:newTransaction];
若不用 mutableArrayValueForKey: 返回的代理对象,addObject: 不会自动发送改变通知。
Manual Change Notification
背景:
In some cases, you may want control of the notification process, for example, to minimize triggering notifications that are unnecessary for application specific reasons, or to group a number of changes into a single notification. Manual change notification provides means to do this.
有的时候你想要控制通知的过程。
有的时候你想屏蔽一些没必要的通知,例如:当且仅当属性值不同时,才发送通知;观察者只注册一个 key,当一组 keys 发生变化时,通知注册了 key 的观察者,免去了需观察多个 key。
实现
-
重写 NSObject 的 +automaticallyNotifiesObserversForKey:,对于不启用自动通知的 keys,返回 NO,对于不处理的 keys,调用 super。
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey { BOOL automatic = NO; if ([theKey isEqualToString:@"balance"]) { automatic = NO; } else { automatic = [super automaticallyNotifiesObserversForKey:theKey]; } return automatic; }
-
手动调用 -willChangeValueForKey, -didChangeValueForKey: 触发通知
- (void)setBalance:(double)theBalance { [self willChangeValueForKey:@"balance"]; _balance = theBalance; [self didChangeValueForKey:@"balance"]; }
-
to-many relationships 不仅仅要指定 key,还要指定改变的类型,改变元素的 indexes
- (void)removeTransactionsAtIndexes:(NSIndexSet *)indexes { [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:@"transactions"]; // Remove the transaction objects at the specified indexes. [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:@"transactions"]; }
Manual Change Notification 优化点
You can minimize sending unnecessary notifications by first checking if the value has changed.
- (void)setBalance:(double)theBalance {
if (theBalance != _balance) {
[self willChangeValueForKey:@"balance"];
_balance = theBalance;
[self didChangeValueForKey:@"balance"];
}
}
group a number of changes into a single notification
(void)setBalance:(double)theBalance {
[self willChangeValueForKey:@"balance"];
[self willChangeValueForKey:@"itemChanged"];
_balance = theBalance;
_itemChanged = _itemChanged+1;
[self didChangeValueForKey:@"itemChanged"];
[self didChangeValueForKey:@"balance"];
}