观察NSMutableArray的插入/移除


76

一个类具有NSMutableArray类型的属性(和实例var),具有综合访问器(通过@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...]吗?

Answers:


79

但是综合访问器是否不应该自动返回这样的代理对象?

没有。

解决此问题的正确方法是什么?我应该编写一个仅调用的自定义访问器[super mutableArrayValueForKey...]吗?

否。实现数组访问器。致电时,KVO将自动发布适当的通知。因此,您要做的就是:

[myObject insertObject:newObject inTheArrayAtIndex:[myObject countOfTheArray]];

正确的事情将自动发生。

为了方便起见,您可以编写addTheArrayObject:访问器。该访问器将调用上述真实数组访问器之一:

- (void) addTheArrayObject:(NSObject *) newObject {
    [self insertObject:newObject inTheArrayAtIndex:[self countOfTheArray]];
}

(您可以并且应该为数组中的对象填充适当的类,以代替NSObject。)

然后,而不是[myObject insertObject:…]您编写[myObject addTheArrayObject:newObject]

不幸的是,最后我检查了add<Key>Object:它的对应remove<Key>Object:对象,它仅被KVO识别为set属性(如NSSet中的属性),而不是数组属性,因此,除非您在它们可以识别的访问器之上实现它们,否则您将无法获得免费的KVO通知。我为此提交了一个错误:x-radar:// problem / 6407437

的所有的访问选择的格式列表上我的博客。


5
第一个问题是,当您添加观察者时,您正在观察某个对象的属性。数组是该属性的,而不是属性本身。这就是为什么您需要使用访问器或-mutableArrayValueForKey:修改数组的原因。
克里斯·汉森

您的最后一点似乎已经过时了-如果我实现了添加和删除访问器,我将获得有关NSArray属性的免费KVO通知。
布赖恩

9

我不会用willChangeValueForKey,并didChangeValueForKey在这种情况下。首先,它们是要指示该路径上的值已更改,而不是表示一对多关系中的值正在更改。willChange:valuesAtIndexes:forKey:如果这样做的话,您可能想使用它。即使这样,使用这样的手动KVO通知也是不好的封装。更好的方法是addSomeObject:在实际拥有数组的类中定义一个方法,其中包括手动KVO通知。这样,将对象添加到数组的外部方法就不必担心处理数组所有者的KVO了,这不是很直观,如果您开始将对象添加到数组中,可能会导致不必要的代码和可能的错误。从几个地方排列。

在此示例中,我实际上将继续使用mutableArrayValueForKey:。我还不能肯定与可变数组,但我相信通过阅读这个方法实际上替换为新对象的整个阵列的文件,所以如果性能是你也想实现一个关注insertObject:in<Key>AtIndex:,并removeObjectFrom<Key>AtIndex:在拥有该数组的类。


7

当您只想观察计数更改时,可以使用聚合键路径:

[myObj addObserver:self forKeyPath:@"theArray.@count" options:0 context:NULL];

但请注意,不会对theArray中的任何重新排序进行触发。


或对此事的替代,例如,使用[-replaceObjectAtIndex:withObject:]时。
Patrick Pijnappel 2014年

3

您对自己问题的答案几乎是正确的。不要theArray对外出售。而是声明一个theMutableArray与没有实例变量对应的不同属性,并编写此访问器:

- (NSMutableArray*) theMutableArray {
    return [self mutableArrayValueForKey:@"theArray"];
}

结果是其他对象可以thisObject.theMutableArray用来对数组进行更改,并且这些更改会触发KVO。

其他答案指出,如果您也实施insertObject:inTheArrayAtIndex:并且removeObjectFromTheArrayAtIndex:仍然正确,则效率会提高。但是,不需要其他对象必须知道这些或直接调用它们。


使用此快捷方式时,我只能观察关键路径“ theArray”(而不是“ theMutableArray”)上的更改。
Michael Mior 2012年

除此之外,所有功能均正常运行,并且我可以theMutableArray像对待NSMutableArray属性一样进行操作。
Michael Mior 2012年

当我尝试延迟初始化此代理对象时,不会发出KVO通知。你知道为什么吗?-(NSMutableArray *)theMutableArray {如果(_theMutableArray)返回_theMutableArray;_theMutableArray = [self mutableArrayValueForKey:@“ theArray”]; 返回_theMutableArray; }
Luong Huy Duc

2

如果您不需要二传手,则还可以使用以下更简单的表格,该表格具有类似的性能(在我的测试中,增长率相同),且样板更少。

// 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"

之所以可行,是因为如果未实现数组访问器并且键没有设置方法,mutableArrayValueForKey:则将查找名称为_<key>或的实例变量<key>。如果找到一个,则代理会将所有消息转发到该对象。

请参阅这些Apple文档,第3部分“有序集合的访问者搜索模式”。


由于某种原因,这对我不起作用。在这种情况下,我可能是真正的盲人。
恩塔尔皮2014年

1
使用此解决方案时,请确保将您的属性标记为readonly,否则它将陷入无限循环...
Symaxion

0

您需要将您的addObject:通话包装willChangeValueForKey:didChangeValueForKey:通话中。据我所知,您无法修改NSMutableArray,以了解是否有任何观察者在监视它的所有者。


0

一种解决方案是使用NSArray并通过插入和删除从头开始创建它,例如

- (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可以比较新旧阵列

注意:self.myArray不应为nil,否则arrayByAddingObject:也会为nil

视情况而定,这可能是解决方案,并且由于NSArray仅存储指针,因此这并不会带来太大的开销,除非您使用大型数组并经常进行操作

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.