5

当调用更新底层数组的方法时,我需要做什么来更新绑定到 NSArrayController 的 tableView?一个例子可以说明这一点。

当我的应用程序启动时,它会创建一个 SubwayTrain。当 SubwayTrain 被初始化时,它会创建一个 SubwayCar。SubwayCar 有一个可变数组“乘客”。初始化地铁车时,会创建乘客数组,并放入几个 People 对象(假设一个名为“ticket collector”的人和另一个名为“homeless guy”的人)。这些家伙总是在 SubwayCar 上,所以我在初始化时创建它们并将它们添加到乘客数组中。

在应用程序的生命周期中,人们上车。在 SubwayCar 上调用“addPassenger”,并将人作为参数传入。

我有一个绑定到 metroTrain.subwayCar.passengers 的 NSArrayController,并且在启动时,我的检票员和无家可归的人表现良好。但是当我使用 [subwayCar addPassenger:] 时,tableView 不会更新。我已经确认乘客肯定已添加到数组中,但 gui 中没有任何更新。

我可能做错了什么?我的直觉是它与 KVO 相关 - 调用 addPassenger 时数组控制器不知道更新(即使 addPassenger 调用 [passengers addObject:]。我可能会出错 - 如果有帮助,我可以发布代码。

感谢任何愿意提供帮助的人。

更新

所以,事实证明我可以通过改变 addPassenger 方法从

[seatedPlayers addObject:person];

NSMutableSet *newSeatedPlayers = [NSMutableSet setWithSet:seatedPlayers];

[newSeatedPlayers addObject:sp];

[seatedPlayers release];

[self setSeatedPlayers:newSeatedPlayers];

我想这是因为我正在使用 [self setSeatedPlayers]。这是正确的方法吗?复制数组、释放旧数组并更新副本(而不是仅仅添加到现有数组)似乎非常麻烦。

4

5 回答 5

7

我不知道它是否被认为是一个错误,但 addObject: (和 removeObject:atIndex:) 不会生成 KVO 通知,这就是数组控制器/表视图没有得到更新的原因。要符合 KVO,请使用 mutableArrayValueForKey:

例子:

[[self mutableArrayValueForKey:@"seatedPlayers"] addObject:person];

您还需要实现 insertObject:inSeatedPlayersAtIndex: 因为默认的 KVO 方法非常慢(它们创建一个全新的数组,将对象添加到该数组,并将原始数组设置为新数组 - 非常低效)

- (void)insertObject:(id)object inSeatedPlayerAtIndex:(int)index
{
   [seatedPlayers insertObject:object atIndex:index];
}

请注意,当数组控制器添加对象时也会调用此方法,因此它也是一个很好的钩子,用于注册撤消操作等。

于 2010-01-15T07:41:22.933 回答
2

我还没有尝试过,所以我不能说它有效,但你不会通过调用来获得 KVO 通知

插入对象:atArrangedObjectIndex:

在 ArrayController 上?

于 2010-01-15T20:12:46.250 回答
1

所以,事实证明我可以通过改变 addPassenger 方法从

[seatedPlayers addObject:person];

NSMutableSet *newSeatedPlayers = [NSMutableSet setWithSet:seatedPlayers];
[newSeatedPlayers addObject:sp];
[seatedPlayers release];
[self setSeatedPlayers:newSeatedPlayers];

我想这是因为我正在使用[self setSeatedPlayers]. 这是正确的方法吗?

首先,它是setSeatedPlayers:, 带有冒号。这在 Objective-C 中非常重要。

使用您自己的设置器是正确的方法,但您使用的是不正确的正确方法。它有效,但您仍然编写的代码超出了您的需要。

您应该做的是实现集合访问器,例如addSeatedPlayersObject:. 然后,给自己发送这条信息。这使得添加人员成为一个简短的单行:

[self addSeatedPlayersObject:person];

而且只要您遵循与 KVC 兼容的访问器格式,您将免费获得 KVO 通知,就像您使用setSeatedPlayers:.

这样做的好处setSeatedPlayers:是:

  • 您用于改变集合的代码会更短。
  • 因为它更短,它会更干净。
  • 使用特定的 set-mutation 访问器提供了特定的 set-mutation KVO 通知的可能性,而不是一般的 the-whole-dang-set-changed 通知。

我也更喜欢这个解决方案mutableSetValueForKey:,这既是为了简洁,也是因为在该字符串文字中拼写错误非常容易。(Uli Kusterer 有一个宏可以在发生这种情况时发出警告,这在您确实需要与 KVC 或 KVO 本身对话时很有用。)

于 2010-01-17T02:42:04.867 回答
1

Key Value Observing的魔力在于Key Value Compliance。您最初使用的方法名称 addObject: 仅与“无序访问器模式”相关联,并且您的属性是索引属性 (NSMutableArray)。当您将属性更改为无序属性 (NSMutableSet) 时,它起作用了。将 NSArray 或 NSMutableArray 视为索引属性,将 NSSet 或 NSMutableSet 视为无序属性。您真的必须仔细阅读本节,才能了解实现魔法所需的条件...... Key-Value-Compliance。即使您不打算使用它们,也有一些针对不同类别的“必需”方法。

于 2012-12-23T21:27:53.313 回答
0
  1. 当更改似乎不会导致 KVO 通知时,使用willChangeValueForKey:didChangeValueForKey:包裹成员的更改。当您直接更改实例变量时,这会派上用场。

  2. 当更改似乎不会导致 KVO 通知时,使用willChangeValueForKey:withSetMutation:usingObjects:didChangeValueForKey:withSetMutation:usingObjects:包裹集合内容的更改。

  3. 用于[seatedPlayers setByAddingObject:sp]使事情更短,并避免不必要地分配可变集。

总的来说,我会这样做:

[self willChangeValueForKey:@"seatedPlayers"
            withSetMutation:NSKeyValueUnionSetMutation 
               usingObjects:sp];
[seatedPlayers addObject:sp];
[self didChangeValueForKey:@"seatedPlayers" 
           withSetMutation:NSKeyValueUnionSetMutation 
              usingObjects:sp];

或这个:

[self setSeatedPlayers:[seatedPlayers setByAddingObject:sp]];

后一种选择会导致自动调用 1 下列出的功能。第一种选择应该更好地执行。

于 2012-02-14T17:08:00.150 回答