Correct implementation of parent/child NSManagedObjectContext
The parent/child MOC model is a really powerful feature of Core Data. It simplifies incredibly the age-old concurrency problem we used to have to deal with. However, as you've stated, concurrency is not your issue. To answer your questions:
- Traditionally, you use the
NSMainQueueConcurrencyType
for theNSManagedObjectContext
associated with the main thread, andNSPrivateQueueConcurrencyType
s for child contexts. The child context does not need to match its parent. TheNSConfinementConcurrencyType
is what allNSManagedObjectContext
s get defaulted to if you don't specify a type. It's basically the "I will managed my own threads for Core Data" type. - Without seeing your code, my assumption would be the scope within which you create the child context ends and it gets cleaned up.
- When using the parent/child context pattern, you need to be using the block methods. The biggest benefit of using the block methods is that the OS will handle dispatching the method calls to the correct threads. You can use
performBlock
for asynchronous execution, orperformBlockAndWait
for synchronous execution.
You would use this such as:
- (void)saveContexts {
[childContext performBlock:^{
NSError *childError = nil;
if ([childContext save:&childError]) {
[parentContext performBlock:^{
NSError *parentError = nil;
if (![parentContext save:&parentError]) {
NSLog(@"Error saving parent");
}
}];
} else {
NSLog(@"Error saving child");
}
}];
}
Now, you need to keep in mind that changes made in the child context (e.g. Entities inserted) won't be available to the parent context until you save. To the child context, the parent context is the persistent store. When you save, you pass those changes up to the parent, who can then save them to the actual persistent store. Saves propogate changes up one level. On the other hand, fetching into a child context will pull data down through every level (through the parent and into the child)
- You need to use some form of
objectWithID
on the managedObjectContext. They are the safest (and really only) way to pass objects around between contexts. As Tom Harrington mentioned in the comments, you may want to useexistingObjectWithID:error:
though becauseobjectWithID:
always returns an object, even if you pass in an invalid ID (which can lead to exceptions). For more details: Link
If you use the parent/child pattern, you usually declare the parent context with
NSMainQueueConcurrencyType
and the child contexts withNSPrivateQueueConcurrencyType
.NSConfinementConcurrencyType
is used for the classic threading pattern.If you want to keep the context, you somehow need a strong reference to it.
You simply call the save method on the child context to push the changes to the parent context, if you want to persist the data, you call save on the parent context as well. You dont need to do this within a block.
-
There are several methods to get an specific object from a context. I cant tell you which one will work in your case, try them out:
- objectRegisteredForID:
- objectWithID:
- existingObjectWithID:error:
I have had similar problems and here are answers to some parts of your questions-
1. You should be able to use concurrency type NSPrivateQueueConcurrencyType
or NSMainQueueConcurrencyType
2. Say you have created a temporary context tempContext
with parent context mainContext
(this is assuming iOS5). In that case, you can just move your managed object from tempContext
to mainContext
by-
object = (Object *)[mainContext objectWithID:object.objectID];
You can then save the mainContext itself.
Maybe also,
[childContext reset];
if you want to reset the temporary context.