我知道我可以将属性设置为新值以获得“设置”更改,但我有兴趣添加,删除,替换更改.如何正确通知NSArray的这些类型的更改?
@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];
观察者类:
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if ([keyPath isEqualToString:@"items"]) { //Not called } }
解决方法
因此,您正在观察Model的实例,以便更改其items属性.如果您希望观察者获得更改通知,您必须确保始终以符合KVO的方式修改items属性.
在我看来,最好的方法是实现mutable indexed collection accessors并始终使用它们来修改属性.所以,你要实现至少其中一个:
- (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,而只显示索引的集合getter:
- (NSUInteger) countOfItems; - (id) objectInItemsAtIndex:(NSUInteger)index;
但是,如果有一个典型的吸气剂,这是没有必要的.
(这些访问器的存在允许你实现一个非NSArray类型的to-many属性.从KVC的角度来看,对于任何实际的数组类型接口都没有必要.)
就个人而言,我不建议这样做,但是一旦你有了这样的访问器,你也可以通过使用-mutableArrayValueForKey获取属性的类似NSMutableArray的代理来改变属性,然后向它发送变异操作.因此,在这种情况下,您可以[[self mutableArrayValueForKey:@“items”] addObject:item].我不喜欢这个,因为我觉得键值编码是指密钥是数据的时候.它是动态的或存储在像NIB这样的数据文件中,在编译时不知道.当您可以选择使用语言符号(例如选择器)来处理属性时,硬编码键名称就是代码气味.
但是,对于在索引访问器方面实现真正曲折的操作(例如排序),这是合理的.
最后,您可以使用NSKeyValueObserving
协议的-willChange …和-didChange …方法在直接修改属性的后备存储时发出更改通知,而无需通过KVO可识别和挂接的突变方法.对于索引的集合属性,可以是-willChange:valuesAtIndexes:forKey:和-didChange:valuesAtIndexes:forKey:methods.就我而言,这是一种更糟糕的代码味道.