在 iOS 开发中,KVO 是一个非常好的工具 (虽然用法比较操蛋)。我们常常用它来分离 Model 和 Controller 之间的一些依赖关系。但是却不太容易对 To-many 关系 (NSArray, NSSet, NSDictionary) 的内容变化进行监听。
Observe NSArray
今天查了官方文档和很多网上资料,推荐一种简单直接的方式对 NSArray 的内容变化进行监听。例如,你需要给 nameArray
增加一个 name
,使用常规方法不会触发 KVO, [nameArray addobject:name]
。
而使用 -mutableArrayValueForKey:
来替代 nameArray
就可以触发 KVO 了,
[[self mutableArrayValueForKey:@"nameArray"] addObject:name];
而且, 观察者得到的 change
字典中,会有详细的修改信息供参考:是增加还是删除还是修改,改了什么,都可以很明确的得到这些信息。例如,
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
if (![keyPath isEqualToString:@"nameArray"]) return;
NSKeyValueChange kindKey = [change[NSKeyValueChangeKindKey] unsignedIntegerValue];
if (kindKey == NSKeyValueChangeInsertion) {
NSString *name = [change[NSKeyValueChangeNewKey] firstObject];
} else if (kindKey == NSKeyValueChangeReplacement) {
NSString *oldName = [change[NSKeyValueChangeOldKey] firstObject];
NSString *newName = [change[NSKeyValueChangeNewKey] firstObject];
}
...
}
副作用: 修改后的 nameArray
是被拷贝的。也是就说内存地址会变化,如果有其他对象也持有这个数组,就要注意不要踩坑。
涉及 KVC 的另一种方式
还有一种方法, 涉及 KVC, 要 override 掉 NSArray 的 Accessors, 我认为很麻烦,而且要写一堆方法在观察者的类中。若无特殊需求, 不推荐这种方式, 处理起来太累了。
Observe NSSet / NSDictionary
NSSet 和 NSArray 差不多。使用 -mutableSetValueForKey:
即可。
NSDictionary 就呵呵了,暂时没发现啥好方法。手动触发吧。
[self willChangeValueForKey:self.dict];
[self.dict setObject:object forKey:key];
[self didChangeValueForKey:self.dict];
参考
NSKeyValueCoding Protocol Reference
KVC Accessors
第九块石头:可变长度对象在KVO中的实现