I'm having an issue with CoreData concurrency on iOS 8.1.
I'm getting the following stack trace for the crash:
NSInternalInconsistencyException - Invalid rowCache row is nil
0 CoreFoundation 0x0000000183b6659c __exceptionPreprocess + 132
1 libobjc.A.dylib 0x00000001942640e4 objc_exception_throw + 56
2 CoreData 0x000000018385b8b8 -[NSSQLCore _newRowCacheRowForToManyUpdatesForRelationship:rowCacheOriginal:originalSnapshot:value:added:deleted:sourceRowPK:properties:sourceObject:newIndexes:reorderedIndexes:] + 6668
3 CoreData 0x00000001838fbea0 -[NSSQLCore recordToManyChangesForObject:inRow:usingTimestamp:inserted:] + 2604
4 CoreData 0x0000000183857638 -[NSSQLCore prepareForSave:] + 1052
5 CoreData 0x00000001838569b4 -[NSSQLCore saveChanges:] + 520
6 CoreData 0x000000018381f078 -[NSSQLCore executeRequest:withContext:error:] + 716
7 CoreData 0x00000001838e6254 __65-[NSPersistentStoreCoordinator executeRequest:withContext:error:]_block_invoke + 4048
8 CoreData 0x00000001838ed654 gutsOfBlockToNSPersistentStoreCoordinatorPerform + 176
9 libdispatch.dylib 0x00000001948a936c _dispatch_client_callout + 12
10 libdispatch.dylib 0x00000001948b26e8 _dispatch_barrier_sync_f_invoke + 72
11 CoreData 0x00000001838e0cb4 _perform + 176
12 CoreData 0x000000018381ec34 -[NSPersistentStoreCoordinator executeRequest:withContext:error:] + 296
13 CoreData 0x0000000183845400 -[NSManagedObjectContext save:] + 1280
This crash happens in several places (about 20), but is most prominent in my data import function which imports 1000s of records and saves every 100.
A few notes about my CoreData setup:
performBlock:
The code where all of these saves are occurring has the following basic pattern:
[backgroundContext performBlock:^{
...
NSArray *result = [backgroundContext fetch...];
...
if ([backgroundContext save:&error]) { // <-- App is crashing here
}
}];
As far as I understand, there shouldn't be any concurrency issues with the NSPersistentStore
behind the backgroundContext
but that's what the crash is telling me.
In addition, this only occurs for less than 0.02% of my user base. It's pretty rare (I'm unable to reproduce) but users are not able to recover from this without deleting and reinstalling the app. It will consistently open and crash for them.
Note, this is only for 64-bit iOS 8.1.X - iOS 7.X and 8.0.X do not exhibit this behavior and I don't see it on anything older than an iPhone 5s.
Clarification: deleting the persistent store fixes this issue for all users. This would seem to indicate it is not a concurrency issue.
Creation of Store & Contexts.
[_persistenStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:[DatabaseManager storeURL]
options:@{NSMigratePersistentStoresAutomaticallyOption:@YES,
NSInferMappingModelAutomaticallyOption:@YES}
error:&error];
Creating Contexts
_backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_backgroundContext setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
[_backgroundContext setPersistentStoreCoordinator:_persistenStoreCoordinator];
if ([_backgroundContext respondsToSelector:@selector(setName:)]) {
[_backgroundContext setName:@"DatabaseManager.BackgroundQueue"];
}
_mainContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_mainContext setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
[_mainContext setPersistentStoreCoordinator:_persistenStoreCoordinator];
if ([_mainContext respondsToSelector:@selector(setName:)]) {
[_mainContext setName:@"DatabaseManager.MainQueue"];
}
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(mainContextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:_mainContext];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(backgroundContextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:_backgroundContext];
Listening for changes
- (void) mainContextDidSave:(NSNotification *)notification
{
[_backgroundContext performBlock:^{
[self->_backgroundContext mergeChangesFromContextDidSaveNotification:notification];
}];
}
- (void) backgroundContextDidSave:(NSNotification*)notification
{
[_mainContext performBlock:^{
NSArray* updated = [notification.userInfo valueForKey:NSUpdatedObjectsKey];
// Fault all objects that will be updated.
for (NSManagedObject* obj in updated) {
NSManagedObject* mainThreadObject = [self->_mainContext objectWithID:obj.objectID];
[mainThreadObject willAccessValueForKey:nil];
}
[self->_mainContext mergeChangesFromContextDidSaveNotification:notification];
}];
}
How code is executed on each of the contexts. I pass a block into here:
- (void)executeBackgroundOperation:(void (^)(NSManagedObjectContext *))operation {
NSAssert(operation, @"No Background operation to perform");
[_backgroundContext performBlock:^{
operation(self->_backgroundContext);
}];
}
- (void)executeMainThreadOperation:(void (^)(NSManagedObjectContext *))operation {
NSAssert(operation, @"No Main Thread operation to perform");
[_mainContext performBlock:^{
operation(self->_mainContext);
}];
}
The only other threads in the crash report that have a lock
in them are two Javascript threads that look like this:
0 libsystem_kernel.dylib 0x3a77cb38 __psynch_cvwait + 24
1 libsystem_pthread.dylib 0x3a7fa2dd pthread_cond_wait + 38
2 libc++.1.dylib 0x39a11e91 _ZNSt3__118condition_variable4waitERNS_11unique_lockINS_5mutexEEE + 34
3 JavaScriptCore 0x2dcd4cb5 _ZN3JSC8GCThread16waitForNextPhaseEv + 102
4 JavaScriptCore 0x2dcd4d19 _ZN3JSC8GCThread12gcThreadMainEv + 50
5 JavaScriptCore 0x2db09597 _ZN3WTFL19wtfThreadEntryPointEPv + 12
6 libsystem_pthread.dylib 0x3a7f9e93 _pthread_body + 136
7 libsystem_pthread.dylib 0x3a7f9e07 _pthread_start + 116
8 libsystem_pthread.dylib 0x3a7f7b90 thread_start + 6
You are correct in thinking this may not be a concurrency issue. Deleting and installing the app isually fixes core data errors when the migration hasn't been done properly.
If you released the app, changed the core data model, then released an update (without migration), this may be the source of your crash.
Follow this tutorial and see if it fixes your issue. http://www.raywenderlich.com/27657/how-to-perform-a-lightweight-core-data-migration
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With