我正在编写一个沙盒 ARC 应用程序,其中包含基于 View 的 NSTableView,它接受拖放文件 (NSURL
)。我在以下 NSTableViewDelegate
方法中遇到了一些明显的奇怪之处:</p>
- (NSView *)tableView:(NSTableView *)tv
viewForTableColumn:(NSTableColumn *)tc
row:(NSInteger)row
{
// `files' is an NSMutableArray* ivar containing NSURLs
// that have been dropped into this table
NSURL *url = [files objectAtIndex:row];
NSString *fileName = [url lastPathComponent];
NSImage *icon = [self iconForURL:url];
NSTableCellView *view = [tv makeViewWithIdentifier:[tc identifier] owner:self];
[[view textField] setStringValue:fileName];
[[view imageView] setImage:icon];
return view;
}
我可以将一个文件拖到 TableView 中,它会正确显示。当我拖动第二个文件时,出现此错误:
*** Canceling drag because exception 'NSRangeException' (reason '*** -[__NSArrayM insertObject:atIndex:]: index 1 beyond bounds for empty array') was raised during a dragging session
通过调试器,我发现在调用 makeViewWithIdentifier:owner:
之后,files
“变空”——实际上变成了一个新的对象实例。我认为这是我不理解的 ARC 的某些方面,但在我看来,该对象对其自己的 ivar 有很强的引用(默认情况下);它怎么可能从我的手下被释放并重新创建?
我想出了两个技巧来解决这个问题:
- 将 ivar 作为表格单元格 View 的所有者传递(希望在未来的版本中它将继续保持强引用);或
- 创建一个局部变量来指向 ivar 的对象,并将 ivar 重新分配给旧对象(这显然是浪费,因为它同时创建了一个替换数组)。
我在这里缺少什么?这些解决方法应该不是必需的。
最佳答案
调用 -makeViewWithIdentifier:owner: 将导致将 -awakeFromNib 消息发送给所有者。这是有记录的,但仅在头文件中(编辑:主文档已更新以引用此内容)。
我想您的文件数组只是在 -awakeFromNib 中重新初始化。
给定情况下的解决方案(加载 View 原型(prototype)而不是 Nib )只是将 nil 作为所有者传递。加载已注册 nib 的其他实现(请参阅 -registerNib:forIdentifier:)可能需要一个所有者,该所有者可能是委托(delegate)(或不是)。因此,可能必须检测和捕获对 -awakeFromNib
的多次调用。设置一个属性来标记 Nib 加载并且只执行一次所需的初始化是很简单的。
请注意,此方法的 Apple 文档已更新以反射(reflect)这一点:
Note that awakeFromNib is called each time this method is called, which means that awakeFromNib is also called on owner, even though the owner is already awake.
关于objective-c - 调用 makeViewWithIdentifier :owner: causes ARC to re-create ivar,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13556165/