I’m implementing a simple file browser (in an NSOutlineView) and am hitting an EXC_BAD_ACCESS when expanding my root node. My NSOutlineViewDataSource returns the children as follows:
- (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];
}
The NSString being returned from this method is being autoreleased, and when the AppKit code calls here:
- (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;
}
The item is already gone. This is an Instruments screenshot of the item’s lifecycle:

I’m not sure what I’m doing wrong – I can’t explicitly retain anything (nor should I!) because ARC is enabled, but still the child item is being lost.
Edit: Stacktrace of the actual crash:
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
Edit 2: Project now attached. Edit the return value of SPFileUtil childrenOfFolder: to use Option 2 to reproduce consistently. It always passes when using a single NSString, but always fails when using the NSFileManager contents.
https://www.dropbox.com/s/lqj5r5ndg0qusak/Spark.zip
The crash occurs right after expanding the root “Files” item.
It’s probably a bit unsafe to provide the outline view contents directly from the filesystem, for example what happens if a file is added/removed from the filesystem? A better approach is for you to cache the filesystem contents and provide contents directly from that. You can then be sure that the value returned from:
Matches the contents you have stored (and you don’t get out-of-bounds exceptions when requesting a row that no longer exists).
To be really fancy you can watch the filesystem and refresh the cache and then the outline view when changes are made to it.
UPDATE: Further to this, as part of the cache you generally build a tree of the nodes in the outline view, using a data structure like this:
and you can set specific nodes in the OutlineView, for example: