NSArrayController 和 KVO
当调用更新底层数组的方法时,我需要做什么来更新绑定到 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
我不知道它是否被认为是一个错误,但是 addObject: (和removeObject:atIndex:) 不会生成 KVO 通知,这就是数组控制器/表视图没有更新的原因。要兼容 KVO,请使用 mutableArrayValueForKey:
示例:
您还需要实现 insertObject:inSeatedPlayersAtIndex: 因为默认的 KVO 方法非常慢(它们创建一个全新的数组,将对象添加到该数组,然后设置原始数组到新数组 - 非常低效)
请注意,当数组控制器添加对象时也会调用此方法,因此它也是一个很好的钩子,可以用于注册撤消操作等。
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:
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)
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.
我还没有尝试过这个,所以我不能说它有效,但是你不会通过调用来获得KVO通知吗
在 ArrayController 上?
I haven't tried this, so I cannot say it works, but wouldn't you get KVO notifications by calling
on the ArrayController?
首先,它是带有冒号的
setSeatedPlayers:
。这在 Objective-C 中至关重要。使用您自己的设置器是正确的方法,但您使用的方法不正确。它有效,但您仍然编写了超出需要的代码。
您应该做的是实现设置访问器,例如
addSeatedPlayersObject:
。然后,向自己发送该消息。这使得添加人员变得很简单:只要您遵循 符合 KVC 的访问器格式,您将免费获得 KVO 通知,就像使用
setSeatedPlayers:
一样。与
setSeatedPlayers:
相比,它的优点是:我也更喜欢这个解决方案,而不是
mutableSetValueForKey:
,既是为了简洁,也是因为很容易拼错该字符串文字中的键。 (Uli Kusterer 有一个宏,可以在发生这种情况时发出警告,当您确实需要与 KVC 或 KVO 本身对话时,这很有用。)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: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: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.)键值观察位于键值合规性。您最初使用的是方法名称 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.
当更改似乎不会导致 KVO 通知时,使用
willChangeValueForKey:
和didChangeValueForKey:
包裹成员的更改。当您直接更改实例变量时,这会派上用场。当更改似乎不会导致 KVO 时,使用
willChangeValueForKey:withSetMutation:usingObjects:
和didChangeValueForKey:withSetMutation:usingObjects:
包裹集合内容的更改通知。使用
[seatedPlayers setByAddingObject:sp]
让事情变得更短,并避免不必要地分配可变集。总的来说,我会这样做:
或这样做:
后一种选择会导致自动调用 1 下列出的函数。第一种选择应该性能更好。
Use
willChangeValueForKey:
anddidChangeValueForKey:
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.Use
willChangeValueForKey:withSetMutation:usingObjects:
anddidChangeValueForKey:withSetMutation:usingObjects:
wrapped around a change of contents of a collection when the change does not appear to cause a KVO notification.Use
[seatedPlayers setByAddingObject:sp]
to make things shorter and to avoid needlessly allocating mutable set.Overall, I'd do either this:
or this:
with the latter alternative causing an automatic invocation of the functions listed under 1. First alternative should be better performing.