Don't observe NSManagedObject for too long

On an off over the years we've seen exceptions and crashes from Sandvox deep in the KVO machinery. Such problems seem semi-random to reproduce, and so are nearly impossible to debug unless you happen to stumble upon a document state that consistently exhibits an issue.

Fortunately, I came across not one, but two such documents very recently.

To cut a long story short, nasty things happen if you're trying to observe certain key paths of a managed object while its context is being torn down.

"What  key paths?” I hear you ask. Well I’m not 100% sure (think it’s dependent and/or compound key paths), but that doesn’t really matter because:

You should never be observing a managed object while its context is being torn down

OK, but let’s say you’ve already done this by accident. How do you track the bug down?

During teardown, the context will turn all objects registered with it back into faults. You can add this snippet to any NSManagedObject subclass to be warned when observation has lasted longer than expected:

You can set a breakpoint of the log statement and use the debugger to introspect what is observing your object longer than expected/desired. Note that there are three reasons for turning an object into a fault:

  • The object is about to become inacessible — it's being deleted from the store, or the context is being torn down
  • The object is being deallocated
  • The context or object is being refreshed — e.g. with -refreshObject:mergeChanges: or -mergeChangesFromContextDidSaveNotification:

In the first two scenarios, having an observer registered is likely an error, but in the third, observation is perfectly legitimate. Sandvox never falls into this code-path (so far!), but your app might do so it’s advisable that you use this snippet only for debugging.

NSTreeController Notes

In the case of Sandvox I found the main cause of this was NSTreeController. Turns out that the controller is often determined to keep observing its content, even after calling [controller setContent:nil].

This becomes a problem if the tree controller happens to outlive the context. Thus the best solution is set the controller’s .managedObjectContext property, and never set it back to nil.

I think this leads to another general rule:

If adding your own KVO or binding directly to a managed object, it’s advisable to also maintain a strong reference to the corresponding context

© Mike Abdullah 2007-2015