【问题标题】:EXC_BAD_ACCESS implementing NSOutlineViewDataSource with ARC enabledEXC_BAD_ACCESS 在启用 ARC 的情况下实现 NSOutlineViewDataSource
【发布时间】:2012-11-12 17:32:35
【问题描述】:

我正在实现一个简单的文件浏览器(在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 屏幕截图:

我不确定我做错了什么 - 我不能明确地 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

在展开根“文件”项后立即发生崩溃。

【问题讨论】:

    标签: objective-c memory-management automatic-ref-counting exc-bad-access nsoutlineview


    【解决方案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;
    }
    

    【讨论】:

    • 谢谢!我曾计划缓存文件系统并定期刷新(或基于每个 FS 事件),但这种快速的脏方法只是一个占位符,它的好奇行为促使我进行调查,而不是继续前进。此外,还添加了堆栈跟踪。
    • 我添加了我的(非常轻量级的)项目的副本。我完全被难住了。 (见编辑 2)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-27
    • 1970-01-01
    • 2011-12-03
    • 2023-03-06
    • 1970-01-01
    相关资源
    最近更新 更多