I have a multi-threaded application that uses Core Data. I saw a lot of startup crashes and various fancy error messages. However, sometimes it works great! I have never seen a crash on my own iPhone4, but it crashes on other devices. I think I figured out this problem, but I donβt know how to solve it best.
When the application starts, I use GCD to download data from the Internet and update the main data in the background stream. In the main thread, I continue to set up table views, and one of these commands starts the NSManagedObjectContext tag. I use pretty much the standard code template, since this is the first time this is executed, the usual lazy instanciation code creates an NSPersistentStoreCoordinator .
However, the background thread creates a new NSManagedObjectContext for its own use. This includes getting the NSPersistentStoreCoordinator as follows:
// Create a new NSManagedObjectContext for this thread NSManagedObjectContext *threadContext = nil; NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator]; if (coordinator != nil) { threadContext = [[NSManagedObjectContext alloc] init]; [threadContext setPersistentStoreCoordinator:coordinator]; NSMergePolicy *mergePolicy = [[NSMergePolicy alloc] initWithMergeType:NSMergeByPropertyObjectTrumpMergePolicyType]; [threadContext setMergePolicy:mergePolicy]; } else { NSLog(@"Error - No NSPersistentStoreCoordinator"); }
I am 90% sure that the problem is that both threads get into the NSPersistentStoreCoordinator code NSPersistentStoreCoordinator same time. Both threads count the nil object, so create an object. This causes problems due to the first thread getting there when ivar suddenly points to the wrong place. Bad things are happening at this moment!
So what is the best way to solve this? I temporarily fixed the problem by adding sleep(1) to the background thread :), but I'm not sure if this is really the best solution! I tried changing the properties of the NSPersistentStoreCoordinator so that they were atomic, but that didn't help. Do I have to wrap each of the standard getters in @synchronise to lock the mutex? I have not had time to try, but it should stop the same code that is triggered by both threads. Or is there a better way?
Thoughts?
Thanks Craig
========================================
For information, here are some of the errors and crashes I've seen. They are pretty random, but it may help others find this post in the future.
2012-01-18 22: 19: 57.586 CBF [10468: 11d03] - [NSSQLModel _addPersistentStore: identifier:]: unrecognized selector sent to instance 0x6b80390
2012-01-18 22: 19: 57.595 CBF [10468: 11d03] * Application termination due to uncaught exception "NSInvalidArgumentException", reason: '- [NSSQLModel _addPersistentStore: identifier:]: unrecognized selector sent to instance 0x6b80390'
2012-01-19 16: 58: 06.671 CBF [11738: fe03] - [__ NSCFDictionary _hasPrecomputedKeyOrder]: unrecognized selector sent to instance 0x6d55040
2012-01-25 21: 45: 31.174 CBF [16911: 1310b] - [__ NSArrayM _addPersistentStore: identifier:]: unrecognized selector sent to instance 0x6d59370
2012-01-25 21: 45: 31.175 CBF [16911: 1310b] * Application terminated due to uncaught exception "NSInvalidArgumentException", reason: '- [__ NSArrayM _addPersistentStore: identifier:]: unrecognized selector sent to instance 0x6d59370'