调用makeViewWithIdentifier:owner:会导致ARC重新创建ivar

时间:2012-11-25 22:21:28

标签: objective-c automatic-ref-counting nstableview

我正在编写一个带有基于视图的NSTableView的沙盒ARC应用程序,该应用程序接受拖放文件(NSURL)。我在以下NSTableViewDelegate方法中遇到了一些重要的陌生感:

- (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;
}

我可以将一个文件拖到表格视图中,然后显示正确。当我拖动第二个文件时,我收到此错误:

*** Canceling drag because exception 'NSRangeException' (reason '*** -[__NSArrayM insertObject:atIndex:]: index 1 beyond bounds for empty array') was raised during a dragging session

通过调试器,我发现files“变为空” - 实际上变成了一个新的对象实例 - 在调用makeViewWithIdentifier:owner:之后。我认为这是我不理解的ARC的某些方面,但在我看来,该对象强烈引用了它自己的ivar(默认情况下);怎么能从我这里得到释放和重新创造?

我已经拿出两个黑客来解决这个问题:

  1. 传递ivar作为表格单元格视图的所有者(希望在将来的版本中它将继续保持强大的引用);或
  2. 创建一个局部变量以指向ivar的对象并将ivar重新分配给旧对象(这显然是浪费,因为它在此期间创建了替换数组)。
  3. 我在这里缺少什么?这些变通办法不是必需的。

1 个答案:

答案 0 :(得分:2)

调用-makeViewWithIdentifier:owner:将导致-awakeFromNib消息发送给所有者。这是记录在案但仅在头文件中(编辑:主文档已更新以引用此内容)。

我想你的文件数组只是在-awakeFromNib中重新初始化。

给定情况下的解决方案(加载视图原型而不是nib)只是将nil作为所有者传递。加载已注册的nib的其他实现(请参阅-registerNib:forIdentifier :)可能需要所有者,该所有者可能是委托(或不是)。因此,可能必须检测并捕获对-awakeFromNib的多次调用。设置一个属性以标记nib加载并且只执行一次所需的初始化是微不足道的。

请注意,此方法的Apple文档已更新,以反映这一点:

  

请注意,每次调用此方法时都会调用awakeFromNib,   这意味着awakeFromNib也会在所有者身上调用,即使是   老板已经醒了。