Reputation: 7493
I am getting an occasional crash report from users (which I'm unable to reproduce myself); it's something to do with NSManagedObjectContext refreshObject
. The crash message is
"An NSManagedObjectContext cannot refresh objects in other contexts"
The code where it crashes is here:
dispatch_async(self.filterMainQueue, ^{
NSArray *items = [Person getAllNonPrivatePersonsWithContext: self.backgroundContextImage];
if (items.count) {
for (Person *person in items) {
[self.backgroundContextImage performBlockAndWait: ^{
[person loadContactReferenceAndImage];
// crashes here
[self.backgroundContextImage refreshObject: person mergeChanges:NO];
}];
}
}
});
+ (NSArray *) getAllNonPrivatePersonsWithContext: (NSManagedObjectContext *) context{
__block NSArray *items = nil;
[context performBlockAndWait: ^{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Person" inManagedObjectContext: context];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
//[fetchRequest setFetchBatchSize: 500];
[fetchRequest setPredicate: [NSPredicate predicateWithFormat: @"isContactPrivate == FALSE"]];
[fetchRequest setReturnsObjectsAsFaults: FALSE];
[fetchRequest setIncludesPendingChanges: FALSE];
NSError *error = nil;
items = [context executeFetchRequest:fetchRequest error:&error];
}];
return items;
}
I'm not quite sure why this is crashing. The backgroundContextImage
is created using NSPrivateQueueConcurrencyType
, and I make sure I'm using performBlockAndWait
when fetching and accessing the managed objects with that context. The filterMainQueue
is a serial queue, to help with doing this work in a background thread instead of the main thread.
The crash report looks like this:
Exception Type: SIGABRT
Exception Codes: #0 at 0x197fb7140
Crashed Thread: 3
Application Specific Information:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'An NSManagedObjectContext cannot refresh objects in other contexts.'
Last Exception Backtrace:
0 CoreFoundation 0x0000000182a98f48 __exceptionPreprocess + 124
1 libobjc.A.dylib 0x000000019764bf80 objc_exception_throw + 52
2 CoreData 0x0000000182786898 -[NSManagedObjectContext refreshObject:mergeChanges:] + 1320
3 CJournal 0x00000001001729c4 __75-[ContactsSyncController loadContactImagesAndLinksWithPermissionWithBlock:]_block_invoke_2 (ContactsSyncController.m:102)
4 CoreData 0x00000001827dc900 developerSubmittedBlockToNSManagedObjectContextPerform + 192
5 libdispatch.dylib 0x0000000197e696a8 _dispatch_client_callout + 12
6 libdispatch.dylib 0x0000000197e74954 _dispatch_barrier_sync_f_invoke + 96
7 CoreData 0x00000001827dc7e8 -[NSManagedObjectContext performBlockAndWait:] + 248
8 CJournal 0x00000001001728bc __75-[ContactsSyncController loadContactImagesAndLinksWithPermissionWithBlock:]_block_invoke (ContactsSyncController.m:97)
9 libdispatch.dylib 0x0000000197e696e8 _dispatch_call_block_and_release + 20
10 libdispatch.dylib 0x0000000197e696a8 _dispatch_client_callout + 12
11 libdispatch.dylib 0x0000000197e756ec _dispatch_queue_drain + 860
12 libdispatch.dylib 0x0000000197e6d1ac _dispatch_queue_invoke + 460
13 libdispatch.dylib 0x0000000197e696a8 _dispatch_client_callout + 12
14 libdispatch.dylib 0x0000000197e77b40 _dispatch_root_queue_drain + 2136
15 libdispatch.dylib 0x0000000197e772dc _dispatch_worker_thread3 + 108
16 libsystem_pthread.dylib 0x000000019807d470 _pthread_wqthread + 1088
17 libsystem_pthread.dylib 0x000000019807d020 start_wqthread + 0
Any ideas?
Upvotes: 1
Views: 1606
Reputation: 7493
The problem was that, under special circumstances, something else in the app would replace the persistentStore and reset the managedObjectContext, and even though the loadContactReferenceAndImage and refreshObject calls are both within a performBlockAndWait. The solution I came up with was to confirm if the managedObjectContext
was the same, and only then refresh the object:
[self.backgroundContextImage performBlockAndWait: ^{
[person loadContactReferenceAndImage];
// MOC could have been updated by another thread
if (person.managedObjectContext == self.backgroundContextImage) {
[self.backgroundContextImage refreshObject: person mergeChanges:NO];
}
}];
Upvotes: 1