0

我有一个对象,它为一个名为contents. 在那些访问器中,当我修改底层数组时,我调用willChange:valuesAtIndexes:forKey:didChange:valuesAtIndexes:forKey:

我还有一个自定义视图对象,它contents通过NSArrayController. 在我见过observeValueForKeyPath:ofObject:change:context:的变化字典中唯一的值是. 当我向数组添加对象时,我希望看到.NSKeyValueChangeKindKeyNSKeyValueChangeSettingNSKeyValueChangeInsertion

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

4

3 回答 3

1

(所有读者请注意:我也讨厌为此使用答案,但是这个讨论太长了,无法发表评论。当然,缺点是它最终没有按时间顺序排序。如果你不喜欢它,我建议你向 Stack Overflow 管理员抱怨评论受到长度限制且仅限纯文本。)

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

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

绑定建立在 KVO 之上。

还有KVC。

所有绑定都是使用实现的observeValueForKeyPath:

覆盖这是一种方式,当然。另一种方法是在具有可绑定属性(视图)的对象中实现访问器。

我的自定义视图提供了应用程序绑定到数组的绑定——或者在本例中为数组控制器。访问器方法适用于 KVC,而不是 KVO。

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

我知道这一点,因为我就是这样做的。请参阅CPU 使用情况中的 PRHGradientView 。

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

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

有相当多的人从事所谓的“过早优化”。如果不问,我无法知道谁是其中之一。

于 2009-02-15T00:30:26.247 回答
0

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

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

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

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

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

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

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

于 2009-02-14T23:39:08.303 回答
0

I have an object that implements the indexed accessor methods for a key called contents. In those accessors, I call willChange:valuesAtIndexes:forKey: and didChange:valuesAtIndexes:forKey: when I modify the underlying array.

Don't do that. KVO posts the notifications for you when you receive a message to one of those accessors.

I also have a custom view object that is bound to contents via an NSArrayController. In observeValueForKeyPath:ofObject:change:context: the only value in the change dictionary for the NSKeyValueChangeKindKey I ever see is NSKeyValueChangeSetting. When I'm adding objects to the array, I expect to see NSKeyValueChangeInsertion.

For one thing, why are you using KVO directly? Use bind:toObject:withKeyPath:options: to bind the view's property to the array controller's arrangedObjects (I assume) property, and implement array accessors (including indexed accessors, if you like) in the view.

For another, remember that arrangedObjects is a derived property. The array controller will filter and sort its content array; the result is arrangedObjects. You could argue that permuting the indexes from the original insertion into a new insertion would be a more accurate translation of the first change into the second, but setting the entire arrangedObjects array was probably simpler to implement (something like [self _setArrangedObjects:[[newArray filteredArrayUsingPredicate:self.filterPredicate] sortedArrayUsingDescriptors:self.sortDescriptors]]).

Does it really matter? Have you profiled and found that your app is slow with wholesale array replacement?

If so, you may need to bind the view directly to the array's content property or to the original array on the underlying object, and suffer the loss of free filtering and sorting.

于 2009-02-14T18:07:07.680 回答