Giter VIP home page Giter VIP logo

coredatafetchedresultscontroller's Introduction

CoreDataFetchedResultsController

A sample app that demonstrates a bug in CoreData using an NSFetchedResultsController with a UITableView.

Steps to reproduce the bug:

  1. Run the sameple app (based on CoreData template)
  2. Tap "+" a few times to add some entries to the table
  3. tap the "+/-1" button in the lower left
  4. tap the "refresh" button in the lower-right

Expected Results:

The first cell in the table view should be removed.

Actual Results:

The first cell is visible and the console log shows "Assertion failure in -[UITableView _endCellAnimationsWithContext]"

Regression:

This bug reproduces in the iOS 5.1 Simulator, 6.0 Simulator, 6.1 Simulator, and a 6.1.4 device.

Notes:

The sample app was created from the CoreData template app.

I added console logging and 2 UIBarButtonItems. The +/-1 button calls doInsertAndDelete:, which creates a new managed object context, then creates a new Event object, deletes the oldest Event object, and updates the timestamps of the other Event objects, then saves its context. The refresh button calls doRefresh:, which creates a new managed object context and updates the timestamps of all Event objects.

The console output when you tap the "+/-1" button is:

01: => (before) mergeChangesFromContextDidSaveNotification
02:   => (enter) controllerWillChangeContent count=4
03:   <= (leave) controllerWillChangeContent count=4
04:     didChangeObject type=1 indexPath=(null) newIndexPath=<NSIndexPath 0x81626d0> 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=<NSIndexPath 0x74874f0> 2 indexes [0, 4] newIndexPath=(null)
10:     didChangeObject type=4 indexPath=<NSIndexPath 0x74875c0> 2 indexes [0, 2] newIndexPath=(null)
11:     didChangeObject type=4 indexPath=<NSIndexPath 0x7479040> 2 indexes [0, 1] newIndexPath=(null)
12:     didChangeObject type=4 indexPath=<NSIndexPath 0x74790f0> 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.

Further use of the table view can trigger assertions, messages to invalid objects, or other badness. I can provide stack traces for various crashes inside UITableView, but the root cause of the problem is the sequence above.

At this point, if you tap the "refresh" button in the sample app, you'll see the following console output:

 => (before) mergeChangesFromContextDidSaveNotification
 <= (after)  mergeChangesFromContextDidSaveNotification
   => (enter) controllerWillChangeContent count=4
   <= (leave) controllerWillChangeContent count=4
     didChangeObject type=4 indexPath=<NSIndexPath 0x74790a0> 2 indexes [0, 2] newIndexPath=(null)
     didChangeObject type=4 indexPath=<NSIndexPath 0x747bb40> 2 indexes [0, 0] newIndexPath=(null)
     didChangeObject type=4 indexPath=<NSIndexPath 0x748dca0> 2 indexes [0, 3] newIndexPath=(null)
     didChangeObject type=4 indexPath=<NSIndexPath 0x7478970> 2 indexes [0, 1] newIndexPath=(null)
   => (enter) controllerDidChangeContent count=4
 *** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-2380.17/UITableView.m:1070
 CoreData: error: Serious application error.  An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:.  Invalid update: invalid number of rows in section 0.  The number of rows contained in an existing section after the update (4) must be equal to the number of rows contained in that section before the update (5), plus or minus the number of rows inserted or deleted from that section (4 inserted, 4 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out). with userInfo (null)

The bug isn't triggered unless you execute an NSFetchRequest within configureCell:atIndexPath: (though I suppose there may be other actions that also trigger the bug). If you look at the implementation of configureCell:atIndexPath:, you'll notice that it conditionally executes a fetch request based on the value of the TRIGGER_BUG precompiler value. Setting TRIGGER_BUG to 0 doesn't execute a fetch request and the app works as you'd expect.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.