I want to change core data objects on the main thread and then save in a background thread. Would the background thread save include the main thread changes?
4 Answers
You can use Core Data in a multithread fashion, but you should follow one of the approaches recommended by Apple:
The pattern recommended for concurrent programming with Core Data is thread confinement: each thread must have its own entirely private managed object context.
There are two possible ways to adopt the pattern:
Create a separate managed object context for each thread and share a single persistent store coordinator. This is the typically-recommended approach.
Create a separate managed object context and persistent store coordinator for each thread. This approach provides for greater concurrency at the expense of greater complexity (particularly if you need to communicate changes between different contexts) and increased memory usage.
Particularly:
Using thread confinement, you should not pass managed objects or managed object contexts between threads. To “pass” a managed object from one context another across thread boundaries, you either:
Pass its object ID (objectID) and use objectWithID: or existingObjectWithID:error: on the receiving managed object context. The corresponding managed objects must have been saved—you cannot pass the ID of a newly-inserted managed object to another context.
Execute a fetch on the receiving context. These create a local version of the managed object in the receiving context.
From this, it ensues that you cannot create managed objects in a thread (with its own context) then save them in the other.
So, in order to accomplish what you would like to, you need to share a managed object context or a persistent store coordinator between threads. In this case you should properly use locking techniques to prevent your store from entering an inconsistent state:
If you share a managed object context or a persistent store coordinator between threads, you must ensure that any method invocations are made from a thread-safe scope. For locking, you should use the NSLocking methods on managed object context and persistent store coordinator instead of implementing your own mutexes. These methods help provide contextual information to the framework about the application's intent—that is, in addition to providing a mutex, they help scope clusters of operations.
I would not suggest to go this route, if you have any chance to modify the design of your app so that you do not need going into locking and synchronization for your core data object.
Please refer following URLs,
1) CORE DATA AND THREADS, WITHOUT THE HEADACHE:
What you are asking for fits right in with nested contexts. You create a private-queue context, and attach it directly to the persistent-store-coordinator. It is simple. Take your current code, and, instead of this...
managedObjectContext = [[NSManagedObjectContext alloc] init];
replace it with this...
workerManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
Now, you have replaced your traditional confinement MOC with a new MOC that will run with its own concurrency queue.
To get your context that you can use from the main thread, you create another managed object context, and make it a child of the one you just created...
managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
managedObjectContext.parent = workerManagedObjectContext;
This means that you can use managedObjectContext just like you were before. Now, however, instead of it going directly to the store, it goes through an intermediary context. The parent context will perform work in its own background thread.
Thus, you can make all the changes to managedObjectContext that you want. When it comes time to save, you do something like this...
static void saveManagedObjectContext(NSManagedObjectContext *moc, void(^completionBlock)(NSError *error)) {
[moc performBlock:^{
NSError *error = nil;
if (moc.hasChanges && [moc save:&error] && moc.parentContext) {
saveManagedObjectContext(moc.parentContext, completionBlock);
} else {
completionBlock(error);
}
}];
}
EDIT
If you want to use this universally, you can easily add it to a category on NSManagedObjectContext, and then just call...
[managedObjectContext saveWithCompletionBlock:^(NSError *error){
if (error) {
// Handle the error
return;
}
// Handle success...
}];