I'm trying to download some JSON objects in the background and am doing quite a bit of multi threading. Once the operation completes, I noticed that this assertion fails:
NSAssert([user.managedObjectContext isEqual:[AppUser managedObjectContext]],@"Different contexts");
How can I merge changes into the main context defined by [AppUser managedObjectContext] ?
I really suggest you to read the following link on importing-and-displaying-large-data-sets-in-core-data by Marcus Zarra.
When you deal with threads, each thread you create needs to have its own context as written in the link which jrturton provided. Then if you want to merge changes between a main context (the one you created in the main thread) and the other context (the one you use in the context), you need to listen for NSManagedObjectContextDidSaveNotification
in the main thread
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contextHasChanged:) name:NSManagedObjectContextDidSaveNotification object:nil];
and do the merge like
- (void)contextHasChanged:(NSNotification*)notification
{
if ([notification object] == [self mainObjectContext]) return;
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:@selector(contextHasChanged:) withObject:notification waitUntilDone:YES];
return;
}
[[self mainObjectContext] mergeChangesFromContextDidSaveNotification:notification];
}
The notification object contains the changes you made in the thread context.
Some notes
Threading is difficult to achieve. The link I provided uses an NSOperation
with its context. This is quite simple to set up but since iOS 5 there are some functionalities that make your life easier.
For example to create a context in a different thread you can do like the following:
// create a context with a private queue so access happens on a separate thread.
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
// insert this context into the current context hierarchy
context.parentContext = context;
// execute the block on the queue of the context
[context performBlock:^{
// do your stuff (e.g. a long import operation)
// save the context here
// with parent/child contexts, saving a context pushes the changes out of the current context
NSError* error = nil;
[context save:&error];
}];
In addition you can see the doc for UIManagedDocument
. This class integrates well for Core Data and allows you to avoid using the Core Data stack.
Hope it helps.
Your particular situation (downloading JSON in the background) is quite common. I have seen a number of implementations that make do without multiple contexts.
In many cases, the simplest and by far the robustest way is to have the objects that do the downloads notify the main thread via protocol or notification of a finished download. You then do the saving on the main thread.
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