双葉幼稚園 A Layman's Repository

A NSFetchedResultsController Pitfall

image

I'm using Core Data and NSFetchedResultsController, and encountered mystery crashes related to them. Here's the solution.

The document from Apple mentions that if delegate is not nil, then NSFetchedResultsController will track changes to managed objects, by listening to notifications sent by managed object context and then call its delegate to modify corresponding UITableView. This implies if you don't set delegate to nil before it gets dealloced, then observation is still registered with NSFetchedResultsController and cause strange messages and crashes. We may see the following messages:

  1. -[SomeStrangeReciver controllerWillChangeContent:]: unrecognized selector sent to instance 0x8251dc0, the receiver is random, I've seen __NSCFType, UITapGestureRecognizer, etc.
  2. CoreData: error: Serious application error. Exception was caught during Core Data change processing. This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification.
  3. Assertion failure in -[UITableView _endCellAnimationsWithContext:] …… Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (1) must be equal to the number of rows contained in that section before the update (0), plus or minus the number of rows inserted or deleted from that section (0 inserted, 0 deleted).
  4. EXCBADACCESS

(These error messages are from Google, and may be slightly different from actual messages)

Though we see different messages, the reason is the same: The observation has not been cancelled after NSFetchedResultsController gets dealloced.

The solution is simple:

- (void)dealloc {
    if (_myFetchedResultsController != nil) {
        _myFetchedResultsController.delegate = nil;
    }

In SSDataKit, the author did the same thing in -dealloc (See SSManagedViewController.m).

So,

  1. Read document carefully, it may have some implication;
  2. Think how it will be implemented;
  3. Memory management in Objective-C is still a little painful;
  4. See how others do it.