What is NSManagedObjectContext's performBlock: used for?
The methods performBlock:
and performBlockAndWait:
are used to send messages to your NSManagedObjectContext
instance if the MOC was initialized using NSPrivateQueueConcurrencyType
or NSMainQueueConcurrencyType
. If you do anything with one of these context types, such as setting the persistent store or saving changes, you do it in a block.
performBlock:
will add the block to the backing queue and schedule it to run on its own thread. The block will return immediately. You might use this for long persist operations to the backing store.
performBlockAndWait:
will also add the block to the backing queue and schedule it to run on its own thread. However, the block will not return until the block is finished executing. If you can't move on until you know whether the operation was successful, then this is your choice.
For example:
__block NSError *error = nil;
[context performBlockAndWait:^{
myManagedData.field = @"Hello";
[context save:&error];
}];
if (error) {
// handle the error.
}
Note that because I did a performBlockAndWait:
, I can access the error outside the block. performBlock:
would require a different approach.
From the iOS 5 core data release notes:
NSManagedObjectContext now provides structured support for concurrent operations. When you create a managed object context using initWithConcurrencyType:, you have three options for its thread (queue) association
Confinement (NSConfinementConcurrencyType).
This is the default. You promise that context will not be used by any thread other than the one on which you created it. (This is exactly the same threading requirement that you've used in previous releases.)
Private queue (NSPrivateQueueConcurrencyType).
The context creates and manages a private queue. Instead of you creating and managing a thread or queue with which a context is associated, here the context owns the queue and manages all the details for you (provided that you use the block-based methods as described below).
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.
NSManagedObjectContext.performBlockAndWait() runs the block on main thread/queue
PerformBlock() and performBlockAndWait() runs on the queue in which your context is created. So if your managedObjectContext is created in main queue it will run in the main queue.
If you want background execution you need to create a background context :
let privateContext = NSManagedObjectContext(
concurrencyType: .PrivateQueueConcurrencyType)
privateContext.persistentStoreCoordinator = mainQueueContext.persistentStoreCoordinator
privateContext.performBlock {
...
}
ManagedObjectContext performBlock(AndWait) deadlock
Your implementation has a number of issues.
Method
diskManagedObjectContext
andbackgroundManagedObjectContext
Use a global concurrent queue, which makes no sense. If you want to make methods thread safe (considering the instance variables are shared resources), you need to use a dedicated queue (either serial or concurrent) and use
dispatch_barrier_sync
to return a value anddispatch_barrier_async
to write a value.Your
saveChanges
method SHOULD be asynchronousA simple and generic implementation may look as follows:
- (void) saveContextChainWithContext:(NSManagedObjectContext*)context
completion:(void (^)(NSError*error))completion
{
[context performBlock:^{
NSError* error;
if ([context save:&error]) {
NSManagedObjectContext* ctx = [context parentContext];
if (ctx) {
dispatch_async(dispatch_get_global(0,0), ^{
[self saveContextChainWithContext:ctx completion:completion];
});
}
else {
if (completion) {
completion(nil);
}
}
}
else {
if (completion) {
completion(error);
}
}
}];
}
Generally, using a synchronous blocking version of a method is always prone to dead locks - even though Core Data strives to avoid such scenarios in a best effort manner.
Thus, think asynchronous wherever possible - and you don't suffer dead locks. Here in your scenario, it's easy to employ a non-blocking asynchronous style.
NSManagedObjectContext and performBlock, changes on iOS 8?
The call -[NSManagedObjectContext init]
is just a wrapper for -[NSManagedObjectContext initWithConcurrencyType:]
with the argument NSConfinementConcurrencyType
. This creates an instance of NSManagedObjectContext
that uses the obsolete thread confinement model - which does not uses a queue. Contexts created using init
or initWithConcurrencyType:
with the value NSConfinementConcurrencyType
passed are not compatible with the queue methods performBlock:
or performBlockAndWait:
.
Create your context using -[NSManagedObjectContext initWithConcurrencyType:]
and pass either NSPrivateQueueConcurrencyType
or NSMainQueueConcurrencyType
as appropriate. The context that is created as a result is compatible with performBlock:
and performBlockAndWait:
and will uses the queue confinement model that was introduced in iOS 5.
Related Topics
Swift - How to Get Last Taken 3 Photos from Photo Library
"Initialize" Class Method for Classes in Swift
Uitextfield - Capture Return Button Event
How to Get Visible Viewcontroller from App Delegate When Using Storyboard
iOS Changing Uiscrollview Scrollbar Color to Different Colors
Ios: Differencebetween -Init and -Viewload of a Viewcontroller
Get Udid of iOS Device Programmatically
iOS Tab Bar Icons Keep Getting Larger
Stretch Background Image for Uibutton
--Resource-Rules Has Been Deprecated in MAC Os X >= 10.10
Space Between Sections in Uitableview
How to Cast an Nsmutablearray to a Swift Array of a Specific Type
Determine Mime Type from Nsdata