【问题标题】:NSArrayController and KVONSArrayController 和 KVO
【发布时间】:2010-01-15 03:55:27
【问题描述】:

当调用更新底层数组的方法时,我需要做什么来更新绑定到 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]。这是正确的方法吗?复制数组、释放旧数组并更新副本(而不是仅仅添加到现有数组)似乎非常麻烦。

【问题讨论】:

  • 表格视图如何绑定到控制器上?您是否将每个表列绑定到 subwayTrain.subwayCar.passengers 的属性(例如,列名称绑定到 subwayTrain.subwayCar.passengers.name)?
  • 没错。乘客姓名会在它启动时显示。

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


【解决方案1】:

我不知道它是否被认为是一个错误,但 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];
}

注意,这个方法也会在数组控制器添加对象时被调用,所以它也是一个很好的钩子,比如注册一个撤销操作等。

【讨论】:

    【解决方案2】:

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

    插入对象:atArrangedObjectIndex:

    在 ArrayController 上?

    【讨论】:

    • 是的,使用 NSArrayController 对数组进行变异使得 NSArrayController 知道变异,并且它(以及绑定到它的东西)应该相应地更新。
    【解决方案3】:

    所以,事实证明我可以通过改变 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];
    

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

    这个比setSeatedPlayers: 的优点是:

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

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

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        【解决方案5】:
        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 中列出的函数。第一种选择应该性能更好。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-19
          • 2012-12-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-11-22
          相关资源
          最近更新 更多