观察一个NSMutableArray插入/删除
一个类有一个types为NSMutableArray的属性(和实例@property
)和合成访问器(通过@property
)。 如果你观察这个数组,使用:
[myObj addObserver:self forKeyPath:@"theArray" options:0 context:NULL];
然后像这样在数组中插入一个对象:
[myObj.theArray addObject:NSString.string];
一个observeValueForKeyPath …通知不会被发送。 但是,以下内容会发送正确的通知:
[[myObj mutableArrayValueForKey:@"theArray"] addObject:NSString.string];
这是因为mutableArrayValueForKey
返回一个代理对象,负责通知观察者。
但是,不应该合成访问器自动返回这样一个代理对象? 什么是正确的方法来解决这个问题 – 我应该写一个自定义访问器,只是调用[super mutableArrayValueForKey...]
?
但是,不应该合成访问器自动返回这样一个代理对象?
没有。
什么是正确的方法来解决这个问题 – 我应该写一个自定义访问器,只是调用
[super mutableArrayValueForKey...]
?
不。实现数组访问器 。 当您拨打这些电话时,KVO会自动发送相应的通知。 所以你所要做的就是:
[myObject insertObject:newObject inTheArrayAtIndex:[myObject countOfTheArray]];
正确的事情会自动发生。
为了方便起见,你可以写一个addTheArrayObject:
accessor。 这个访问器会调用上面描述的真实数组访问器之一:
- (void) addTheArrayObject:(NSObject *) newObject { [self insertObject:newObject inTheArrayAtIndex:[self countOfTheArray]]; }
(你可以也应该填入数组中对象的适当的类,代替NSObject
。)
然后,而不是[myObject insertObject:…]
,你写[myObject addTheArrayObject:newObject]
。
不幸的是, add<Key>Object:
及其对应的remove<Key>Object:
是最后一次检查,只能由KVO设置(如在NSSet中)属性,而不是数组属性,所以您不能获得免费的KVO通知除非您在其所承认的访问器之上实现它们。 我提出了一个错误:x-radar:// problem / 6407437
我有我的博客上所有访问器select器格式的列表 。
在这种情况下,我不会使用willChangeValueForKey
和didChangeValueForKey
。 一方面,它们是为了表明在这条道路上的价值已经发生了变化,而不是在一对多关系中的价值正在发生变化。 你会想要使用willChange:valuesAtIndexes:forKey:
相反,如果你这样做了。 即便如此,使用像这样的手动KVO通知是不好的封装。 一个更好的方法是在实际拥有该数组的类中定义一个addSomeObject:
方法,其中包含手动KVO通知。 这样,向数组中添加对象的外部方法也不需要担心处理数组所有者的KVO,这不会很直观,并且如果您开始向对象添加对象,可能会导致不需要的代码和可能的错误arrays从几个地方。
在这个例子中,我将实际上继续使用mutableArrayValueForKey:
我不太肯定可变数组,但是我相信通过阅读文档,这个方法实际上是用一个新的对象replace整个数组,所以如果性能是一个问题,你还需要实现insertObject:in<Key>AtIndex:
和removeObjectFrom<Key>AtIndex:
在拥有该数组的类中。
当您只想观察更改的计数时,您可以使用聚合键path:
[myObj addObserver:self forKeyPath:@"theArray.@count" options:0 context:NULL];
但是请注意,arrays中的任何重新sorting都不会触发。
你自己对自己问题的回答几乎是正确的。 不要在外面theArray
。 相反,声明一个不同的属性, theMutableArray
,对应于没有实例variables,并写这个访问器:
- (NSMutableArray*) theMutableArray { return [self mutableArrayValueForKey:@"theArray"]; }
结果是其他对象可以使用thisObject.theMutableArray
来更改数组,并且这些更改会触发KVO。
其他答案指出,如果你还实现了insertObject:inTheArrayAtIndex:
和removeObjectFromTheArrayAtIndex:
仍然是正确的,那么效率会提高。 但是不需要其他对象必须知道这些或者直接调用它们。
如果你不需要setter,你也可以使用下面更简单的表格,它具有类似的性能(在我的testing中相同的增长率 )和较less的样板。
// Interface @property (nonatomic, strong, readonly) NSMutableArray *items; // Implementation @synthesize items = _items; - (NSMutableArray *)items { return [self mutableArrayValueForKey:@"items"]; } // Somewhere else [myObject.items insertObject:@"test"]; // Will result in KVO notifications for key "items"
这是有效的,因为如果数组访问器没有被实现并且没有setter,那么mutableArrayValueForKey:
将会查找名为_<key>
或<key>
的实例variables。 如果find一个,代理将把所有消息转发给这个对象。
请参阅这些Apple文档 ,第3部分“订购集合的访问者search模式”部分。
您需要在willChangeValueForKey:
和didChangeValueForKey:
调用中包装您的addObject:
call。 据我所知,你正在修改的NSMutableArray没有办法知道任何观察者看着它的主人。
一个解决scheme是使用NSArray,并通过插入和删除从头开始创build它
- (void)addSomeObject:(id)object { self.myArray = [self.myArray arrayByAddingObject:object]; } - (void)removeSomeObject:(id)object { NSMutableArray * ma = [self.myArray mutableCopy]; [ma removeObject:object]; self.myArray = ma; }
比你得到的KVO,可以比较新旧arrays
注意:self.myArray不应该是零,否则arrayByAddingObject:结果也是零
根据情况,这可能是解决scheme,因为NSArray只存储指针,这不是一个开销,除非你使用大型数组和频繁的操作