KVO修改由NSMutableArray支持的NSArray的通知

我正在尝试使用KVO来侦听NSArray属性上的集合更改事件。 公开的说,这个属性是一个只读的NSArray,但是由一个NSMutableArray ivar支持,所以我可以修改这个集合。

我知道我可以将属性设置为一个新值来获得“设置”更改,但我有兴趣添加,删除,replace更改。 如何正确地通知NSArray的这些types的更改?

@interface Model : NSObject @property (nonatomic, readonly) NSArray *items; @end @implementation Model { NSMutableArray *_items; } - (NSArray *)items { return [_items copy]; } - (void)addItem:(Item *)item { [_items addObject:item]; } @end 

 Model *model = [[Model alloc] init]; [observer addObserver:model forKeyPath:@"items" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:NULL]; Item *item = [[Item alloc] init]; [model addItem:newItem]; 

观察class:

 -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if ([keyPath isEqualToString:@"items"]) { //Not called } } 

首先,你应该明白,KVO是用来观察物体的属性变化的 。 也就是说,你不能“观察一个数组”,你会观察到一个索引的集合属性。 该属性可以由数组支持或以其他方式实现。 只要符合KVC标准并按照KVO标准进行修改,就足够了。 (所以,如果属性的types是NSArray*或者使用NSMutableArray*或其他types来实现,则无关紧要。)

所以,你正在观察一个Model的实例来更改它的items属性。 如果您希望观察者获得更改通知,则必须始终以符合KVO的方式修改items属性。

在我看来,最好的办法是实现可变索引的集合访问器,并总是使用它们来修改属性。 所以,你至less要实现其中一个:

 - (void) insertObject:(id)anObject inItemsAtIndex:(NSUInteger)index; - (void) insertItems:(NSArray *)objects atIndexes:(NSIndexSet *)indexes; 

其中之一:

 - (void) removeObjectFromItemsAtIndex:(NSUInteger)index; - (void) removeItemsAtIndexes:(NSIndexSet *)indexes; 

当属性由NSMutableArray支持时,上述方法是围绕_items上相应方法的直接包装。

你写任何其他方法来修改你的财产应该通过其中之一。 所以,你的-addItem:方法是:

 - (void)addItem:(Item *)item { [self insertObject:item inItemsAtIndex:[_items count]]; } 

您也可以删除items属性的普通getter,而只是公开索引集合获得者:

 - (NSUInteger) countOfItems; - (id) objectInItemsAtIndex:(NSUInteger)index; 

如果有一个典型的getter,那不是必须的。

(这些访问器的存在允许你实现一个非NSArraytypes的to-many属性,从KVC的angular度来看,对于任何实际的数组types的接口是没有必要的)。

就个人而言,我不build议这样做,但是一旦有了这样的访问器,也可以通过使用-mutableArrayValueForKey:获取属性的-mutableArrayValueForKey: NSMutableArray的代理来改变属性,然后向其发送变异操作。 所以,在这种情况下,你可能会[[self mutableArrayValueForKey:@"items"] addObject:item] 。 我不喜欢这个,因为我觉得键值编码适用于密钥是数据的情况。 它是dynamic的或存储在数据文件中,如NIB,在编译时不知道。 当你有select使用语言符号(例如select器)来解决属性的硬编码键名称是一种代码异味。

但是,对于那些在索引访问方面真正曲折的操作,比如分类,这是可以certificate的。

最后,您可以使用NSKeyValueObserving协议的-willChange...-didChange...方法在直接修改属性的后备存储时发出更改通知,而无需通过KVO可以识别并挂接的突变方法。 对于索引集合属性,这将是-willChange:valuesAtIndexes:forKey:-didChange:valuesAtIndexes:forKey:方法。 就我而言,这是一种更糟糕的代码味道。