observeValueForKeyPath:ofObject:change:context:与数组无法正常工作

时间:2009-02-14 17:27:07

标签: cocoa cocoa-bindings key-value-observing nsarraycontroller

我有一个对象,它实现了名为contents的键的索引访问器方法。在这些访问器中,我在修改底层数组时调用willChange:valuesAtIndexes:forKey:didChange:valuesAtIndexes:forKey:

我还有一个通过contents绑定到NSArrayController的自定义视图对象。在observeValueForKeyPath:ofObject:change:context:中,我看到的NSKeyValueChangeKindKey更改字典中的唯一值是NSKeyValueChangeSetting。当我向数组添加对象时,我希望看到NSKeyValueChangeInsertion

每次插入单个项目时重新创建我观察的对象的内部表示 - 特别是当我批量加载数百个项目时 - 会出现相当大的性能问题,正如您所想象的那样。 Cocoa似乎认为每次添加或删除单个项目时都会设置一个全新的数组,我做错了什么?

3 个答案:

答案 0 :(得分:1)

(请注意所有读者:我也讨厌使用这些答案,但这种讨论对于评论来说太长了。当然,缺点是它最终没有按时间顺序排序。如果你不喜欢它,我建议你向Stack Overflow管理员抱怨注释长度有限且仅限纯文本。)

  

我不明白你在视图中实现数组访问器是什么意思。

为您作为绑定公开的可变数组属性实现访问器,包括索引访问器。

  

Bindings建立在KVO之上。

和KVC。

  

所有绑定都是使用observeValueForKeyPath:

实现的

覆盖这是一种方式,当然。另一种方法是使用bindable属性(视图)在对象中实现访问器。

  

我的自定义视图提供了应用绑定到数组的绑定 - 或者在这种情况下是数组控制器。访问方法适用于KVC,而不适用于KVO。

Cocoa Bindings将为您调用视图的访问者(可能使用KVC)。您不需要实现KVO观察方法(当然,除非您直接使用KVO)。

我知道这是因为我已经这样做了。请参阅CPU Usage中的PRHGradientView。

奇怪的是,文档没有提到这一点。我要提交一个关于它的文档错误 - 要么我正在做一些脆弱的事情,要么他们忘记在文档中提到这个非常好的功能。

  

绝对重要的是我在每次更新阵列时都会收到一条消息。如果没关系,我不会把它作为一个问题发布。

有相当多的人从事“过早优化”的事情。我没办法知道谁是其中之一。

答案 1 :(得分:0)

  

我有一个对象,它实现了名为contents的键的索引访问器方法。在那些访问器中,我调用willChange:valuesAtIndexes:forKey:和didChange:valuesAtIndexes:forKey:当我修改底层数组时。

不要那样做。当您收到其中一个访问者的消息时,KVO会为您发布通知。

  

我还有一个通过NSArrayController绑定到内容的自定义视图对象。在observeValueForKeyPath:ofObject:change:context:我看到的NSKeyValueChangeKindKey的更改字典中唯一的值是NSKeyValueChangeSetting。当我向数组添加对象时,我希望看到NSKeyValueChangeInsertion。

首先,你为什么直接使用KVO?使用bind:toObject:withKeyPath:options:将视图的属性绑定到数组控制器的arrangedObjects(我假设)属性,并在视图中实现数组访问器(包括索引访问器,如果您愿意)。

另一方面,请记住arrangedObjects是派生属性。阵列控制器将对其内容数组进行过滤和排序;结果是arrangedObjects。您可以争辩说,将索引从原始插入置换为新插入将更准确地将第一个更改转换为第二个更改,但设置整个arrangedObjects数组可能更容易实现(类似{{1 }})。

真的重要吗?你有没有找到并发现批发阵列替换你的应用程序很慢?

如果是这样,您可能需要将视图直接绑定到数组的[self _setArrangedObjects:[[newArray filteredArrayUsingPredicate:self.filterPredicate] sortedArrayUsingDescriptors:self.sortDescriptors]]属性或底层对象上的原始数组,并且会丢失免费过滤和排序。

答案 2 :(得分:0)

我出于本问题范围之外的原因手动调用KVO方法。我已禁用自动观察此属性。我知道我在那里做什么。

我不明白你在视图中实现数组访问器是什么意思。绑定建立在KVO之上。所有绑定都是使用observeValueForKeyPath:实现的。我的自定义视图提供了应用程序绑定到数组的绑定 - 或者在本例中是一个数组控制器。访问方法适用于KVC,而不适用于KVO。

绝对重要的是我在每次更新阵列时都会收到一条消息。如果没关系,我不会把它作为一个问题发布。我称之为

[[modelObject mutableArrayValueForKey:@"contents"] addObjectsFromArray:hundredsOfObjects];

在每次插入时,我的视图都会观察到一个全新的数组。由于我可能会添加数百个对象,因此O(N^2)应为O(N)。这是完全不可接受的,抛开性能问题。但是,既然你提到它,视图就必须做大量工作来观察一个全新的数组,这会大大减慢程序的速度。

我不能放弃使用数组控制器,因为我需要过滤和排序,因为有一个NSTableView绑定到同一个控制器。我依靠它来保持排序和选择同步。

我用一个完整的黑客解决了我的问题。我编写了一个单独的方法,手动调用KVO方法,以便只发送一个KVO消息。这是一个黑客,我不喜欢它,它仍然使我的视图重新读取整个阵列 - 虽然现在只有一次 - 但它现在有效,直到我找到一个更好的解决方案。