In the previous section we saw how to consume changes from one context into another. With iOS 5.0, Core Data added another option to handle change notifications. Instead of having every context talk directly to NSPersistentStoreCoordinator, it’s possible to chain contexts together in a parent/child design.
Each child context will request objects from its parent context, and if that parent context doesn’t have the objects requested, it will pass that request up the chain until it hits NSPersistentStoreCoordinator and I/O is performed. However, if the parent context does have a reference to the desired objects, they’re returned to the child without I/O being performed.
Further, when a child makes a change to an object and then calls -save:, no I/O is performed as a result of that call. This means child contexts can make changes to objects and pass them to their parent without a performance penalty. In addition, the change notifications are handled automatically by the context so you don’t need to observe the notifications and then consume them.
To define a context as a child of another context, the setup is slightly different:
| NSManagedObjectContext *moc = ...; //The existing context |
| NSManagedObjectContext *child = nil; |
| NSManagedObjectContextConcurrencyType type = NSPrivateQueueConcurrencyType; |
| child = [[NSManagedObjectContext alloc] initWithConcurrencyType:type]; |
| [child setParentContext:moc]; |
Instead of the child being associated with NSPersistentStoreCoordinator via -setPersistentStoreCoordinator:, it’s associated with another context via a call to -setParentContext:. This tells NSManagedObjectContext that when it makes a request or saves an object, those requests and/or changes need to be pushed to the parent context.
The result is that it’s possible to have contexts that can make changes and push them up to another context without having to write out to disk via NSPersistentStoreCoordinator. If the child context is a child of the main queue context that’s associated with the user interface, the child context can process data on a background queue (by being created as an NSPrivateQueueConcurrencyType context), and when a save occurs, the user interface is notified immediately.
There are a few things to be aware of with this design. First, the changes aren’t actually persisted until the changes are passed to an NSPersistentStoreCoordinator. This means that if a change is made in a child context and passed to the main queue context but the main queue context isn’t saved before the application exits, those changes will be lost. If the application is set up to save the main queue context on exit, this isn’t normally an issue unless the application terminates abruptly.
The second issue is that this concept of automatic change notification goes in only one direction—from child to parent. If a change is made in the parent and even if the parent is saved, the child isn’t notified of that change. If the child already had a reference to the object that was changed, then the child wouldn’t receive those updates. A merge conflict could result if the child also attempted to alter that object.
There are many benefits to using this parent/child design. First, without having to listen for notifications, the amount of code that needs to be written is reduced. As part of that, the notification process is much faster. The contexts talking internally is going to be faster than code that you can write that talks to them externally.
Since child contexts request their data from their parent first, they can be much faster in retrieving objects if they are already in memory. For situations where you want to process data that already exists in memory, it makes sense to spawn a private child context and let that context process the data off the main queue. Ideally, no I/O will take place during that data processing.
Yet another benefit is in data consumption. When a request is made to the Internet and the resulting data needs to be consumed, it can be consumed with a child context with minimal impact to the main queue and user interface.
In the next few sections, we’ll demonstrate using the parent/child design.