NSArrayController 和 KVO

发布于 2024-08-18 15:37:35 字数 1112 浏览 7 评论 0原文

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

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

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

我有一个绑定到 SubwayTrain.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] 这是正确的方法吗?复制数组、释放旧数组并更新副本(而不是仅仅添加到现有数组)是很麻烦的。

What do I need to do to update a tableView bound to an NSArrayController when a method is called that updates the underlying array? An example might clarify this.

When my application launches, it creates a SubwayTrain. When SubwayTrain is initialised, it creates a single SubwayCar. SubwayCar has a mutable array 'passengers'. When a Subway car is initialised, the passengers array is created, and a couple of People objects are put in (let's say a person with name "ticket collector" and another, named "homeless guy"). These guys are always on the SubwayCar so I create them at initialisation and add them to the passengers array.

During the life of the application people board the car. 'addPassenger' is called on the SubwayCar, with the person passed in as an argument.

I have an NSArrayController bound to subwayTrain.subwayCar.passengers, and at launch my ticket collector and homeless guy show up fine. But when I use [subwayCar addPassenger:], the tableView doesn't update. I have confirmed that the passenger is definitely added to the array, but nothing gets updated in the gui.

What am I likely to be doing wrong? My instinct is that it's KVO related - the array controller doesn't know to update when addPassenger is called (even though addPassenger calls [passengers addObject:]. What could I be getting wrong here - I can post code if it helps.

Thanks to anyone willing to help out.

UPDATE

So, it turns out I can get this to work by changing by addPassenger method from

[seatedPlayers addObject:person];

to

NSMutableSet *newSeatedPlayers = [NSMutableSet setWithSet:seatedPlayers];

[newSeatedPlayers addObject:sp];

[seatedPlayers release];

[self setSeatedPlayers:newSeatedPlayers];

I guess this is because I am using [self setSeatedPlayers]. Is this the right way to do it? It seems awfully cumbersome to copy the array, release the old one, and update the copy (as opposed to just adding to the existing array).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

罪#恶を代价 2024-08-25 15:37:35

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

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

I don't know if its considered a bug, but addObject: (and removeObject:atIndex:) don't generate KVO notifications, which is why the array controller/table view isn't getting updated. To be KVO-compliant, use mutableArrayValueForKey:

Example:

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

You'll also want to implement insertObject:inSeatedPlayersAtIndex: since the default KVO methods are really slow (they create a whole new array, add the object to that array, and set the original array to the new array -- very inefficient)

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

Note that this method will also be called when the array controller adds objects, so its also a nice hook for thinks like registering an undo operation, etc.

醉南桥 2024-08-25 15:37:35

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

插入对象:atArrangedObjectIndex:

在 ArrayController 上?

I haven't tried this, so I cannot say it works, but wouldn't you get KVO notifications by calling

insertObject:atArrangedObjectIndex:

on the ArrayController?

念三年u 2024-08-25 15:37:35

所以,事实证明我可以通过 addPassenger 方法进行更改来使其工作

[seatedPlayers addObject:person];

NSMutableSet *newSeatedPlayers = [NSMutableSet setWithSet:seatedPlayers];
[newSeatedPlayers addObject:sp];
[就座玩家发布];
[自我设置SeatedPlayers: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 本身对话时,这很有用。)

So, it turns out I can get this to work by changing by addPassenger method from

[seatedPlayers addObject:person];

to

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

I guess this is because I am using [self setSeatedPlayers]. Is this the right way to do it?

First off, it's setSeatedPlayers:, with the colon. That's vitally important in Objective-C.

Using your own setters is the correct way to do it, but you're using the incorrect correct way. It works, but you're still writing more code than you need to.

What you should do is implement set accessors, such as addSeatedPlayersObject:. Then, send yourself that message. This makes adding people a short one-liner:

[self addSeatedPlayersObject:person];

And as long as you follow the KVC-compliant accessor formats, you will get KVO notifications for free, just as you do with setSeatedPlayers:.

The advantages of this over setSeatedPlayers: are:

  • Your code to mutate the set will be shorter.
  • Because it's shorter, it will be cleaner.
  • Using specific set-mutation accessors provides the possibility of specific set-mutation KVO notifications, instead of general the-whole-dang-set-changed notifications.

I also prefer this solution over mutableSetValueForKey:, both for brevity and because it's so easy to misspell the key in that string literal. (Uli Kusterer has a macro to cause a warning when that happens, which is useful when you really do need to talk to KVC or KVO itself.)

眼角的笑意。 2024-08-25 15:37:35

键值观察位于键值合规性。您最初使用的是方法名称 addObject: ,该方法仅与“无序访问器模式”相关联,并且您的属性是索引属性(NSMutableArray)。当您将属性更改为无序属性(NSMutableSet)时,它就起作用了。将 NSArray 或 NSMutableArray 视为索引属性,将 NSSet 或 NSMutableSet 视为无序属性。您确实必须仔细阅读本节才能知道实现奇迹需要什么... 键值合规性。即使您不打算使用它们,不同类别也有一些“必需”方法。

The key to the magic of Key Value Observing is in Key Value Compliance. You initially were using a method name addObject: which is only associated with the "unordered accessor pattern" and your property was an indexed property (NSMutableArray). When you changed your property to an unordered property (NSMutableSet) it worked. Consider NSArray or NSMutableArray to be indexed properties and NSSet or NSMutableSet to be unordered properties. You really have to read this section carefully to know what is required to make the magic happen... Key-Value-Compliance. There are some 'Required' methods for the different categories even if you don't plan to use them.

只等公子 2024-08-25 15:37:35
  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 下列出的函数。第一种选择应该性能更好。

  1. Use willChangeValueForKey: and didChangeValueForKey: wrapped around a change of a member when the change does not appear to cause a KVO notification. This comes in handy when you are directly changing an instance variable.

  2. Use willChangeValueForKey:withSetMutation:usingObjects: and didChangeValueForKey:withSetMutation:usingObjects: wrapped around a change of contents of a collection when the change does not appear to cause a KVO notification.

  3. Use [seatedPlayers setByAddingObject:sp] to make things shorter and to avoid needlessly allocating mutable set.

Overall, I'd do either this:

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

or this:

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

with the latter alternative causing an automatic invocation of the functions listed under 1. First alternative should be better performing.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文