Assertion failure in -[UITableView _endCellAnimationsWithContext:] with NSFetchedResultsController

前端 未结 1 899
灰色年华
灰色年华 2021-01-13 19:58

I have 2 managed object contexts: (1) created as NSMainQueueConcurrencyType that is used by the UI/main thread and (2) created as NSPrivateQueueConcurrenc

相关标签:
1条回答
  • 2021-01-13 20:23

    NSFetchedResultsController gets terribly confused if you use an NSFetchRequest when preparing your cell in response to tableView:cellForRowAtIndexPath:. If you don't execute a NSFetchRequest, all is well. However, if you do, it triggers NSFetchedResultsController to perform further change notifications, which does bad things to UITableView.

    The workaround for this is to set includesPendingChanges = NO on your NSFetchRequest.

    I have opened a radar issue about this -- problem id 14048101 -- with a detailed example and sample app. This bug reproduces on iOS 5.1, 6.0, and 6.1.

    In my sample app, I added logging to Xcode's CoreData template to log enter/leave of NSFetchedResultsController delegate methods. When I insert + delete objects on the network context, the logging shows:

    01: => (before) mergeChangesFromContextDidSaveNotification 02: => (enter) controllerWillChangeContent count=4 03: <= (leave) controllerWillChangeContent count=4 04: didChangeObject type=1 indexPath=(null) newIndexPath= 2 indexes [0, 0] 05: => (enter) controllerDidChangeContent count=5

    At this point, all is good. controllerDidChangeContent: has been called to process the 1 insert, which calls [tableView endUpdates], which calls tableView:cellForRowAtIndexPath:, which calls configureCell:atIndexPath:.

    06: => (enter) configure cell at row 0

    At this point, configureCell:atIndexPath: creates an NSFetchRequest and calls [self.managedObjectContext executeFetchRequest:error:] -- here begins the badness. Executing this fetch request triggers the processing of the remaining changes in the context (1 delete and 3 updates) before processing of the insert has finished (we entered controllerDidChangeContent: on line #05 and don't leave until line #16).

    07: => (enter) controllerWillChangeContent count=5 08: <= (leave) controllerWillChangeContent count=5 09: didChangeObject type=2 indexPath= 2 indexes [0, 4] newIndexPath=(null) 10: didChangeObject type=4 indexPath= 2 indexes [0, 2] newIndexPath=(null) 11: didChangeObject type=4 indexPath= 2 indexes [0, 1] newIndexPath=(null) 12: didChangeObject type=4 indexPath= 2 indexes [0, 3] newIndexPath=(null) 13: => (enter) controllerDidChangeContent count=4

    At this point, the framework is making a re-entrant call tocontrollerDidChangeContent:.

    14: <= (leave) controllerDidChangeContent count=4 15: <= (leave) configure cell at row 0 16: <= (leave) controllerDidChangeContent count=4 17: <= (after) mergeChangesFromContextDidSaveNotification

    At this point, you can see in the UI that: (1) a new cell has been added, (2) 3 cells were updated, and (3) the deleted cell is still visible, which is wrong.

    After further action in the UI, I typically get an Assertion failure or message sent to an invalid object exception.

    My sample app is available at https://github.com/peymano/CoreDataFetchedResultsController

    0 讨论(0)
提交回复
热议问题