NSFetchedResultsController: Crashes when using cache with private queue confinement

Originator:quellish
Number:rdar://17283442 Date Originated:12-Jun-2014 00:38 AM
Status:Open Resolved:
Product:iOS SDK Product Version:7.0
Classification:Crash Reproducible:Always
 
Summary:
Use of nested NSManagedObjectContexts using private queue confinement crashes NSFetchedResultsController when the cache is enabled. With the cache disabled, or a single non-nested NSManagedObjectContext, it does not crash.

Steps to Reproduce:
1. Build the project.
2. Run the project in the iOS Simulator.
3. Tap the + to add a few new objects. 
4. Quit the simulated application.
5. Open the application again or run again from Xcode.

Expected Results:
Fetched results controller should populate with appropriate cached data, and not crash.

Actual Results:
The application will crash on the first attempt to access the fetchedObjects of the fetched results controller (inside configureCell:atIndexPath:). The stack trace is as follows:

2014-06-12 00:07:09.695 FRCCacheCrasher[76359:60b] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 0 beyond bounds for empty array'
*** First throw call stack:
(
	0   CoreFoundation                      0x0000000101cba495 __exceptionPreprocess + 165
	1   libobjc.A.dylib                     0x0000000101a1999e objc_exception_throw + 43
	2   CoreFoundation                      0x0000000101c72e3f -[__NSArrayI objectAtIndex:] + 175
..... many other unhelpful lines that radar ate. 

Stopping at that line in the debugger or setting an exception breakpoint allows us to inspect what is going on.
The NSFetchedResultsController cache has been loaded and populated the _NSDefaultSectionInfo object, however the NSFetchedResultsController at this point has no fetchedObjects, and the objects of the section info object appear to be invalid. Attempting to access them will throw an exception.

(lldb) po [[self fetchedResultsController] fetchedObjects]
<__NSArrayI 0x109300290>(

)


(lldb) po [[self fetchedResultsController] sections]
<__NSArrayM 0x10977bb20>(
<_NSDefaultSectionInfo: 0x10977be60>
)


(lldb) po [[[self fetchedResultsController] sections] lastObject]
<_NSDefaultSectionInfo: 0x10977be60>

(lldb) po [[[[self fetchedResultsController] sections] lastObject] objects]
error: Execution was interrupted, reason: internal ObjC exception breakpoint(-3)..
The process has been returned to the state before expression evaluation.
(lldb) 

It appears that when the fetch is performed on the NSFetchedResultsController the cache is loaded and _NSDefaultSectionInfo populated with bad data. This happens wether performFetch: is called from the main thread or through the context's performBlock: .

Disabling the NSFetchedResultsController cache will prevent this issue from occuring.

Version:
iOS 7.0, iOS 7.1

Notes:
The included project is based on the Core Data Master-Detail template provided by Xcode 5.1.1. Minimal changes have been made to demonstrate this issue:
- The application delegate creates an NSManagedObjectContext that uses private queue confinement, not thead confinement. It also creates a child of that context, which is passed to the view controller. Bug does not seem to happen when there is no child context.
- The Core Data Master-Detail template code for the Master view controller has been moved to the class CoreDataTemplateTableViewController
- MasterViewController is a subclass of CoreDataTemplateTableViewController, overriding the NSFetchedResultsControllerDelegate methods to dispatch calls to the table view on the main queue, as with private queue confinement the callbacks will be coming from another thread.
- The "cacheName" method was added to make it easy to turn the cache on and off. Have this method return nil to disable the cache.

Configuration:
iPhone 5s, iOS Simulator 5.1.1

Attachments:
'FRCCacheCrasher.zip' was successfully uploaded.

Comments


Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!