观察一个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器格式的列表 。

在这种情况下,我不会使用willChangeValueForKeydidChangeValueForKey 。 一方面,它们是为了表明在这条道路上的价值已经发生了变化,而不是在一对多关系中的价值正在发生变化。 你会想要使用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只存储指针,这不是一个开销,除非你使用大型数组和频繁的操作