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

Kev*_*lia 9 objective-c key-value-observing nsarray ios ios7

我正在尝试使用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
Run Code Online (Sandbox Code Playgroud)

Model *model = [[Model alloc] init];

[observer addObserver:model 
      forKeyPath:@"items" 
         options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) 
         context:NULL];

Item *item = [[Item alloc] init];
[model addItem:newItem];
Run Code Online (Sandbox Code Playgroud)

观察者类:

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

Ken*_*ses 19

首先,您应该了解KVO用于观察对象的属性变化.也就是说,您不能"观察数组",您会观察索引的集合属性.该属性可以由数组支持或以其他方式实现.只要它符合KVC并以符合KVO标准的方式进行修改,这就足够了.(因此,如果属性是类型NSArray*或使用NSMutableArray*或任何东西实现,则无关紧要.)

因此,您正在观察Modelitems属性更改的实例.如果您希望观察者获得更改通知,则必须确保始终items以符合KVO的方式修改属性.

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

- (void) insertObject:(id)anObject inItemsAtIndex:(NSUInteger)index;
- (void) insertItems:(NSArray *)objects atIndexes:(NSIndexSet *)indexes;
Run Code Online (Sandbox Code Playgroud)

其中一个:

- (void) removeObjectFromItemsAtIndex:(NSUInteger)index;
- (void) removeItemsAtIndexes:(NSIndexSet *)indexes;
Run Code Online (Sandbox Code Playgroud)

当属性由a支持时NSMutableArray,上面的方法是围绕相应方法的简单包装器_items.

您编写的用于修改属性的任何其他方法都应该通过其中一种方法.所以,你的-addItem:方法是:

- (void)addItem:(Item *)item {
    [self insertObject:item inItemsAtIndex:[_items count]];
}
Run Code Online (Sandbox Code Playgroud)

您还可以删除items属性的普通getter ,而只显示索引的集合getter:

- (NSUInteger) countOfItems;
- (id) objectInItemsAtIndex:(NSUInteger)index;
Run Code Online (Sandbox Code Playgroud)

但是,如果有一个典型的吸气剂,这是没有必要的.

(这些访问器的存在允许你实现一个非类型的to-many属性NSArray.从KVC的角度来看,对于任何实际的数组类型接口都没有必要.)

就个人而言,我不建议这样做,但是一旦你有了这样的访问器,你也可以通过获取属性的NSMutableArray-like代理并使用-mutableArrayValueForKey:变量操作向其发送变异操作来改变属性.所以,在这种情况下,你可能会这样做[[self mutableArrayValueForKey:@"items"] addObject:item].我不喜欢这个,因为我觉得键值编码是关键是数据的时候.它是动态的或存储在像NIB这样的数据文件中,在编译时不知道.当您可以选择使用语言符号(例如选择器)来处理属性时,硬编码键名是代码气味.

但是,对于在索引访问器方面实现真正曲折的操作(例如排序),这是合理的.

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

  • 我刚刚做了一个KVOMutableArray(https://github.com/haifengkao/KVOMutableArray)来实现@Ken的想法. (6认同)