启用ARC后实现NSOutlineViewDataSource时出现EXC_BAD_ACCESS错误

3

我正在实现一个简单的文件浏览器(在NSOutlineView中),当展开我的根节点时,遇到了EXC_BAD_ACCESS错误。我的NSOutlineViewDataSource返回子项如下:

- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item {
    if (!item) {
        // Root node
        return @"Files";
    }
    NSFileManager *manager = [NSFileManager defaultManager];

    NSError *error = nil;
    return [[manager contentsOfDirectoryAtPath:@"/" error:&error] objectAtIndex:index];
}

这个方法返回的NSString是自动释放的,当AppKit代码调用这里时:
- (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item {
    NSString *identifier = [item isEqualToString:@"Files"] ? @"HeaderCell" : @"DataCell";
    NSTableCellView *cell = [outlineView makeViewWithIdentifier:identifier owner:self];
    cell.textField.stringValue = item;
    return cell;
}

item已经消失了。这是项目生命周期的Instruments截图:

Instruments Screenshot

我不确定我做错了什么——我不能明确地retain任何东西(也不应该这样做!)因为启用了ARC,但是子项仍然会丢失。

编辑:实际崩溃的堆栈跟踪:

   0 CoreFoundation -[__NSCFString retain]
   1 Spark -[SPFileBrowserController outlineView:child:ofItem:] /Users/Craig/projects/Spark/Spark/SPFileBrowserController.m:29
   2 AppKit loadItemEntryLazyInfoIfNecessary
   3 AppKit -[NSOutlineView _rowEntryForChild:ofParent:requiredRowEntryLoadMask:]
   4 AppKit -[NSOutlineView _expandItemEntryChildren:atStartLevel:expandChildren:andInvalidate:]
   5 AppKit -[NSOutlineView _expandItemEntry:expandChildren:startLevel:]
   6 AppKit -[NSOutlineView _batchExpandItemsWithItemEntries:expandChildren:]
   7 AppKit -[NSOutlineView expandItem:expandChildren:]
   8 AppKit -[NSOutlineView _doUserExpandOrCollapseOfItem:isExpand:optionKeyWasDown:]
   9 AppKit -[NSOutlineView _outlineControlClicked:]
  10 AppKit -[NSApplication sendAction:to:from:]
  11 AppKit -[NSControl sendAction:to:]
  12 AppKit -[NSCell _sendActionFrom:]
  13 AppKit -[NSCell trackMouse:inRect:ofView:untilMouseUp:]
  14 AppKit -[NSButtonCell trackMouse:inRect:ofView:untilMouseUp:]
  15 AppKit -[NSControl mouseDown:]
  16 AppKit -[NSWindow sendEvent:]
  17 AppKit -[NSApplication sendEvent:]
  18 AppKit -[NSApplication run]
  19 AppKit NSApplicationMain
  20 libdyld.dylib start
编辑2: 项目现在已附加。将SPFileUtil childrenOfFolder:的返回值编辑为使用选项2以保持一致性。当使用单个NSString时,它总是通过,但是当使用NSFileManager内容时,它总是失败。https://www.dropbox.com/s/lqj5r5ndg0qusak/Spark.zip

扩展根目录下的“文件”项目后崩溃发生。

1个回答

1

直接从文件系统提供大纲视图内容可能有点不安全,例如如果文件系统中添加/删除了文件会发生什么?更好的方法是缓存文件系统内容并直接从中提供内容。然后您可以确保从以下值返回:

– numberOfRowsInTableView:

匹配您存储的内容(当请求不存在的行时,不会出现越界异常)。

如果想要更加高级,您可以监视文件系统并在对其进行更改时刷新缓存和大纲视图。

更新:作为缓存的一部分,通常会使用像这样的数据结构构建大纲视图中节点的树:

@interface OutlineNode : NSObject
{
    id _item;
    OutlineNode * __weak _parentNode;
    NSMutableArray *_childNodes;
}

你可以在OutlineView中设置特定的节点,例如:

- (BOOL)outlineView:(NSOutlineView *)outlineView
   isItemExpandable:(id)item
{
    if (item == nil)
        return YES;

    OutlineNode *node = (OutlineNode *)item;
    return node.parentNode == nil;
}

谢谢!我原本计划缓存文件系统并定期刷新(或基于每个文件系统事件),但这种快速而简单的方法只是一个占位符,它的奇怪行为促使我进行调查,而不仅仅是继续前进。另外,堆栈跟踪已添加。 - Craig Otis
我添加了我的(非常轻量级的)项目的副本。我完全被难住了。(请参见编辑2) - Craig Otis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接