当调用更新底层数组的方法时,如何更新绑定到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]。这是正确的方法吗?复制数组,释放旧数组并更新副本(而不是仅添加到现有数组)似乎非常麻烦。
答案 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通知
ArrayController上的insertObject:atArrangedObjectIndex:
?
答案 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:
的优点是:
我也更喜欢这个解决方案而不是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)
当更改未显示导致KVO通知时,请使用willChangeValueForKey:
和didChangeValueForKey:
包围成员的更改。当您直接更改实例变量时,这会派上用场。
当更改似乎不会导致KVO通知时,使用willChangeValueForKey:withSetMutation:usingObjects:
和didChangeValueForKey:withSetMutation:usingObjects:
包围集合内容的更改。
使用[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中列出的函数。第一个替代应该表现更好。