我正在尝试使用KVO来侦听NSArray属性上的集合更改事件。在公开场合,该属性是一个只读的NSArray,但是由NSMutableArray ivar支持,以便我可以修改该集合。
我知道我可以将属性设置为新值以获得“设置”更改,但我有兴趣添加,删除,替换更改。如何正确通知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
}
}
答案 0 :(得分:19)
首先,您应该了解KVO用于观察对象以了解其属性的变化。也就是说,你不能观察阵列"因此,您会观察索引的集合属性。该属性可以由数组支持或以其他方式实现。只要它符合KVC并以符合KVO标准的方式进行修改,这就足够了。 (因此,如果属性属于NSArray*
类型或使用NSMutableArray*
或其他任何内容实现,则无关紧要。)
因此,您正在观察Model
的{{1}}属性更改的实例。如果您希望观察者获得更改通知,您必须确保始终以符合KVO的方式修改items
属性。
在我看来,最好的方法是实现mutable indexed collection accessors并始终使用它们来修改属性。因此,您至少要实施以下其中一项:
items
其中一个:
- (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]];
}
属性的普通getter,而只显示索引的集合getter:
items
但是,如果有一个典型的吸气剂,则没有必要。
(这些访问器的存在允许您实现非- (NSUInteger) countOfItems;
- (id) objectInItemsAtIndex:(NSUInteger)index;
类型的多对象属性。从KVC的角度来看,没有必要任何实际的数组类型接口。)
就个人而言,我不建议这样做,但是一旦你拥有了这样的访问者,你也可以通过使用NSArray
获取属性的NSMutableArray
- 代理然后发送来改变属性突变操作。因此,在这种情况下,您可能会-mutableArrayValueForKey:
。我不喜欢这样,因为我觉得键值编码是指密钥是数据的时候。它是动态的或存储在像NIB这样的数据文件中,在编译时是未知的。当您可以选择使用语言符号(例如选择器)来处理属性时,硬编码密钥名称就是代码气味。
但是,对于在索引访问器方面实现真正曲折的操作(例如排序),这是合理的。
最后,当您直接修改属性的后备存储而不通过时,您可以使用NSKeyValueObserving
协议的[[self mutableArrayValueForKey:@"items"] addObject:item]
和-willChange...
方法发出更改通知KVO可识别并挂钩的变异方法。对于索引集合属性,可以是-didChange...
和-willChange:valuesAtIndexes:forKey:
方法。就我而言,这是一种更糟糕的代码味道。