在Cocoa KVO中,为什么对NSMutableArray代理的更改不会通知观察者?

6
我正在iOS中实现一个名为DocumentsManager的类,并希望创建一个to-many属性documents,使其符合KVO规范。它似乎主要工作正常,我的KVO访问器和修改器方法也被调用。然而,令我困扰的是,在直接对由调用我的实例的mutableArrayValueForKey:返回的NSMutableArray代理进行更改时,不会通知观察者。
因此,这段代码会通知我插入@"aaa",但不会通知插入@"bbb",尽管它们都实际上被插入并在docsProxy中可见。这是预期行为吗?如果是,使用mutableArrayValueForKey:方法的好处是什么?
NSMutableArray *docsProxy = [[DocumentsManager instance] mutableArrayValueForKey:@"documents"];
[[DocumentsManager instance] addObserver:self forKeyPath:@"documents" options:NSKeyValueObservingOptionNew context:NULL];

[[DocumentsManager instance] insertObject:@"aaa" inDocumentsAtIndex:0]; // OK
[docsProxy insertObject:@"bbb" atIndex:0];                              // no notification!

好问题!听起来 KVO 可能应该 在这种情况下工作。 - Jasper Blues
1个回答

5
原来,mutableArrayValueForKey: 方法并不总是返回一个可通知的数组。只有在观察对象上已经注册了观察者时,它才会返回可通知的数组!
因此,将我的前两行代码交换一下就可以解决这个问题:
[[DocumentsManager instance] addObserver:self forKeyPath:@"documents" options:NSKeyValueObservingOptionNew context:NULL];
NSMutableArray *docsProxy = [[DocumentsManager instance] mutableArrayValueForKey:@"documents"];

如果我们能够阅读这些方法的源代码,我们会节省多少时间啊……


2
我们确实这样做了,回过头来看,这是为什么显而易见。KVO创建了一个神奇的子类并实现了isa-swizzle(这是有文档记录的)。因此,如果您已经创建了代理,那么在交换后就无法更新它们。https://developer.apple.com/library/ios/documentation/cocoa/conceptual/KeyValueObserving/Articles/KVOImplementation.html - Rob Napier
如果在创建代理时已经注册了第一个观察者,而我又添加了另一个观察者,则第二个观察者也会收到通知。即使在创建时没有注册任何观察者,人们仍可以想象代理仍然具有指向原始对象的指针,以便在未注册任何观察者时检查可能的监听器。 - Jean-Philippe Pellet
1
我已经考虑了一段时间并编写了一些测试代码。我开始相信这可能是KVO的错误。代理调用insertObject:inDocumentsAtIndex:,就像您的直接调用一样,而且这个方法应该被KVO自动子类化。我如何到达insertObject:inDocumentsAtIndex:不重要;它应该表现出相同的行为。我可能会为此打开一个radar(bugreport.apple.com)。 - Rob Napier

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接