NSArrayController和KVO

时间:2010-01-15 03:55:27

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

当调用更新底层数组的方法时,如何更新绑定到NSArrayController的tableView?一个例子可能会澄清这一点。

当我的应用程序启动时,它会创建一个SubwayTrain。当SubwayTrain初始化时,它会创建一个SubwayCar。 SubwayCar有一个可变的阵列'乘客'。当地铁车辆初始化时,乘客阵列被创建,并且放入几个人物对象(假设有一个名为“票务收集者”的人和另一个名为“无家可归者”的人)。这些人总是在SubwayCar上,所以我在初始化时创建它们并将它们添加到乘客阵列。

在应用程序的生命周期中,人们登上了汽车。在SubwayCar上调用'addPassenger',并将此人作为参数传递。

我有一个NSArrayController绑定到subwayTrain.subwayCar.passengers,并在启动时我的票务收集器和无家可归的人显示正常。但是当我使用[subwayCar addPassenger:]时,tableView不会更新。我已经确认乘客肯定已添加到阵列中,但没有任何内容在gui中更新。

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

感谢愿意帮忙的人。

更新

所以,事实证明我可以通过改变

中的addPassenger方法来实现这一点
[seatedPlayers addObject:person];

NSMutableSet *newSeatedPlayers = [NSMutableSet setWithSet:seatedPlayers];

[newSeatedPlayers addObject:sp];

[seatedPlayers release];

[self setSeatedPlayers:newSeatedPlayers];

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

5 个答案:

答案 0 :(得分: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];
}

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

答案 1 :(得分:1)

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

  

insertObject:atArrangedObjectIndex:

ArrayController上的

答案 2 :(得分:1)

  

所以,事实证明我可以通过改变

中的addPassenger方法来实现这一点
[seatedPlayers addObject:person];
     

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

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

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

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

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

[self addSeatedPlayersObject:person];

只要您关注the KVC-compliant accessor formats,您就会免费获得KVO通知,就像使用setSeatedPlayers:一样。

这超过setSeatedPlayers:的优点是:

  • 改变集合的代码会更短。
  • 因为它更短,所以会更清洁。
  • 使用特定的set-mutation访问器提供了特定的set-mutation KVO通知的可能性,而不是一般的整个dang-set-changed通知。

我也更喜欢这个解决方案而不是mutableSetValueForKey:,这只是为了简洁起见,因为它很容易拼错字符串文字中的键。 (Uli Kusterer has a macro to cause a warning when that happens,当你确实需要与KVC或KVO本身交谈时很有用。)

答案 3 :(得分:1)

Key Value Observing魔力的关键在于Key Value Compliance。您最初使用的是方法名称addObject:它只与“无序访问器模式”相关联,并且您的属性是索引属性(NSMutableArray)。当您将属性更改为无序属性(NSMutableSet)时,它可以正常工作。将NSArray或NSMutableArray视为索引属性,将NSSet或NSMutableSet视为无序属性。你必须仔细阅读这一部分,以了解使魔术发生的必要条件...... Key-Value-Compliance。即使您不打算使用它们,也有一些针对不同类别的“必需”方法。

答案 4 :(得分:0)

  1. 当更改未显示导致KVO通知时,请使用willChangeValueForKey:didChangeValueForKey:包围成员的更改。当您直接更改实例变量时,这会派上用场。

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

  3. 使用[seatedPlayers setByAddingObject:sp]缩短时间,避免不必要地分配可变集。

  4. 总的来说,我会这样做:

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

    或者这个:

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

    后一种选择导致自动调用1中列出的函数。第一个替代应该表现更好。

相关问题