Q:

iOS 8 App crashing when resetting my NSManagedObjectContext while in a performBlock

While my one of my NSManagedObjectContext's is running its performBlock the user has chosen to sign out of their account (which will reset all contexts and delete the persistent stores). This reset code is obviously being run at a similar time as the performBlock, and so the app crashes with the exception:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Object's persistent store is not reachable from this NSManagedObjectContext's coordinator'
The exact location it crashes seems to be when the one context saves in its performBlock, which will then send out the did save notification, and merge into the other context. Here's the stack trace:
0   CoreFoundation                      0x03b96df6 __exceptionPreprocess + 182

1   libobjc.A.dylib                     0x03820a97 objc_exception_throw + 44

2   CoreData                            0x034f7791 _PFRetainedObjectIDCore + 1169

3   CoreData                            0x034f72f0 -[NSManagedObjectContext(_NSInternalAdditions) _retainedObjectWithID:] + 32

4   CoreData                            0x034db0f3 -[NSManagedObjectContext objectWithID:] + 595

5   CoreData                            0x0352e14e _faultBatchAtIndex + 1102

6   CoreData                            0x0352ed02 -[_PFBatchFaultingArray objectAtIndex:] + 50

7   CoreData                            0x035dd010 +[NSFetchedResultsController(PrivateMethods) _insertIndexForObject:inArray:lowIdx:highIdx:sortDescriptors:] + 144

8   CoreData                            0x035d7ba2 -[NSFetchedResultsController(PrivateMethods) _postprocessInsertedObjects:] + 738

9   CoreData                            0x035da3dd __77-[NSFetchedResultsController(PrivateMethods) _managedObjectContextDidChange:]_block_invoke + 2285

10  CoreData                            0x034e7454 developerSubmittedBlockToNSManagedObjectContextPerform + 196

11  CoreData                            0x034e7337 -[NSManagedObjectContext performBlockAndWait:] + 231

12  CoreData                            0x035d9acf -[NSFetchedResultsController(PrivateMethods) _managedObjectContextDidChange:] + 127

13  Foundation                          0x00eff929 __57-[NSNotificationCenter addObserver:selector:name:object:]_block_invoke + 40

14  CoreFoundation                      0x03b61974 __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 20

15  CoreFoundation                      0x03a4f61b _CFXNotificationPost + 3051

16  Foundation                          0x00eeef26 -[NSNotificationCenter postNotificationName:object:userInfo:] + 98

17  CoreData                            0x034c84d3 -[NSManagedObjectContext(_NSInternalNotificationHandling) _postObjectsDidChangeNotificationWithUserInfo:] + 83

18  CoreData                            0x034d9cbe -[NSManagedObjectContext _mergeChangesFromDidSaveDictionary:usingObjectIDs:] + 3934

19  CoreData                            0x034d8d40 -[NSManagedObjectContext mergeChangesFromContextDidSaveNotification:] + 496

20  App                              0x00319707 __57-[ContextManager contextDidSavePrivateQueueContext:]_block_invoke + 103

This is iOS 8 only - iOS 7 works fine. What I can't figure out is if this is a bug with iOS 8 that I'm feeling the effects of, or if Apple have introduced "new features" into Core Data that has broken our reset flow. Can anyone shed some light on this?

A:

I fixed the bug by adding a category on NSManagedObjectContext and adding a flag using objc_setAssociatedObject magic. The flag is for whether the context is safe to perform any performBlock that comes its way. For that, I added another method on the category called safePerformBlock which looks at the flag. If it's false, I return out immediately, rather than processing the block that was passed in.
When I delete the persistent store on the contexts, the flag is set to unsafe. When the user then logs back in and the persistent store is recreated, the flag is set to safe.
Essentially, I'm treating this flag on the context as a cancellation token on the performBlocks. Why there is nothing in the API for this I don't know. Under the circumstances, this is the best fix I could find.

