rentzsch.com: tales from the red shed

NSOutlineView and Core Data

Cocoa

Ow! Writing Core Data applications that use NSOutlineView can be painful. Here’s what I learned:

  1. You must engage NSZombieEnabled. This is nonnegotiable.

    I’m 99% confident if you’re not using NSZombieEnabled with your NSOutlineView-using Core Data app, your app has subtle bugs (in the form of messaging objects that have been deallocated).

    That’s not your fault — even Apple can’t get it right (see the next point). But it is your fault if you don’t leverage NSZombieEnabled to hunt them down and put them on a short leash.

  2. Abandon NSTreeController. Perhaps 10.5 will change the equation, but today you must forget NSTreeController exists.

    Why? If you follow point 1, you already know: NSTreeController has retention bugs which cause it to message deallocated objects. NSZombieEnabled screams when NSTreeController walks into the room. And keeps on screaming.

    NSTreeController is so sexy, you’ll Want To Believe. I personally kept patching up NSTreeController with categories to calm her down and make her work. But the categories grew as I hit more edge cases (especially with Drag and Drop).

    I decided while old-school data sources are more code and decidedly less cool, I had little choice if I wanted reliable software that wouldn’t break on each Mac OS X point release. I strongly suggest you too ignore NSTreeController’s siren song.

  3. Core Data was not born like other girls. Sometimes it doesn’t completely mesh with other parts of AppKit, especially the older parts. Enter NSOutlineView.

    Since you can’t use NSTreeController, that means you’re implementing an old-fashioned data source. Sounds ok: Core Data holding your objects, NSOutlineView displaying them: your data source the center of it all.

    The issue is that NSOutlineView knows about but doesn’t retain objects returned from -outlineView:child:ofItem:. On the other side, Core Data will hand you fetched objects in a autorelease pool. Thus, by the time the user interacts with your NSOutlineView, those objects have been deallocated. (Again, see the importance of NSZombieEnabled to catch this stuff.)

    It’s up to you to keep her down. I do this by stuffing the objects I return from -outlineView:child:ofItem: into an NSMutableArray so they don’t go away. I -removeAllObjects whenever I get a -outlineView:numberOfChildrenOfItem: with a nil item value so I don’t leak.

Will garbage collection make all this easier for Cocoa App Developers Of The Future?

Yeah.

Can you name the tune?

Update: Scott Stevenson thinks I’m being too hard on poor ol’ NSTreeController. I probably am. Let me add nuance to my statements so they’re neither black nor white.

You can successfully use NSTreeController if your needs are simple — basic display and in-place editing work. What breaks NSTreeController is the complicated stuff: medium-to-heavy programmatic or user-originated tree structure mutation. If you have a more-or-less static tree to display, NSTreeController can be made to work.

My advice then is knowing NSTreeController’s limits. When you hit those limits, do not try to cope with them. Rip NSTreeController out straight away and take the coding hit. Rolling your own stable outline data source is cheaper than tangling with NSTreeController. Trust me, I’ve been down that path a few times. Don’t do what Jonny Don’t Does.

Update: Blake nailed it. She’s a snarl-toothed seether.

(If I’m cursed with a penchant for dredging up cheesy lyrics, the least I can do is force them upon you by clumsily lodging them into otherwise salient writings. Now you know why I can’t do book deals.)

Sunday, January 21, 2007
12:00 AM