The following code works well without the last statement. However, with the last line, Xcode stops and shows the following message:
CoreData`+[NSManagedObjectContext Multithreading_Violation_AllThatIsLeftToUsIsHonor]:
NSManagedObjectContext *context = GLOBAL_appDelegate.coreDataHelper.contextBackground;
[context performBlock:^{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:@"CdTag" inManagedObjectContext:context]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"title BEGINSWITH[cd] %@", @"webpages"];
[fetchRequest setPredicate:predicate];
NSArray *cdTags = [context executeFetchRequest:fetchRequest error:nil];
NSLog(@"number of tags: %li", cdTags.count);
CdTag *cdTag = [cdTags objectAtIndex:0];
// Accessing a property causes the assertion warning to be shown
cdTag.title;
}];
I'm using Xcode 7 beta 5 and I have multi-threading assertions enabled.
The contexts are defined as follows:
_model = [NSManagedObjectModel mergedModelFromBundles:nil];
_coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:_model];
_context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_context setPersistentStoreCoordinator:_coordinator];
_contextBackground = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_contextBackground setParentContext:_context];
Is this an Xcode bug or do I do something wrong?
Could you please also add the code where the context initialisation happens?
The error you get is related to accessing a managed object context from a thread/queue other than the one it was initialised on.
What usually happens is that the managed object context is initialised using the default/legacy "confinement type" concurrency type for which the
performBlock:
and
performBlockAndWait:
methods won't actually do what they advertise they do i.e. make sure the context is accessed on the queue it was associated with upon initialisation.
If what I suggested above is what is actually happening you could (assuming everything else is in place) fix this by initialising your context like so:
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
or
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
Here's the docs on concurrency strategies:
When you create a managed object context using initWithConcurrencyType:, you have three options for its thread (queue) association
Confinement (NSConfinementConcurrencyType)
For backwards compatibility, this is the default. You promise that context will not be used by any thread other than the one on which you created it. In general, to make the behavior explicit you’re encouraged to use one of the other types instead.
You can only use this concurrency type if the managed object context’s parent store is a persistent store coordinator.
Private queue (NSPrivateQueueConcurrencyType)
The context creates and manages a private queue.
Main queue (NSMainQueueConcurrencyType)
The context is associated with the main queue, and as such is tied into the application’s event loop, but it is otherwise similar to a private queue-based context. You use this queue type for contexts linked to controllers and UI objects that are required to be used only on the main thread.
If you use contexts using the confinement pattern, you send the contexts messages directly; it’s up to you to ensure that you send the messages from the right queue.
You use contexts using the queue-based concurrency types in conjunction with performBlock: and performBlockAndWait:.
Apple Docs
As a side note, the error message you get is one of the last few "easter eggs" still standing inside Cocoa.
The problem does no longer occur when building for iOS 9.0.
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