
Multi-Context in Core Data
- Transfer
Hello.
When you start using CoreData to store data in your applications, you start working with a single managed objective context (MOC). This is what is used in the template when creating a project in xCode, if you check the box next to "Use Core Data" when creating a project.

Using CoreData in combination with NSFetchedResultsController greatly simplifies the work with any kind of list of items that are displayed on the screen in a table view.
There are two scenarios in which you would like to branch out, i.e. using several controlled objective contexts: 1) to simplify the process of adding / editing new elements and 2) to avoid blocking the UI. In this post I want to consider ways to create your contexts in order to get what you want.
First, let's look at setting a single context. You need a persistent store coordinator (PSC) to access the database file on disk. So that this coordinator understands how this database is structured, you need a model. This model is combined from all the model definitions contained in the project and indicates to CoreData about this database structure. The coordinator is installed on the managed context object through the function property. Remember the first rule: a managed objective context will be written to disk using the coordinator if you call saveContext.

Consider this scheme. Each time you insert, update or delete an entity in this single controlled objective context, the selected results controller will be notified of these changes and will update its table view content. This is independent of context preservation. You can save as rarely or as often as you want. The Apple template saves on every addition of an entity and also (not strange) on applicationWillTerminate.
This approach is mainly suitable for most basic cases, but as I said before, there are two problems with it. The first is related to adding a new entity. You probably want to use the same bby / visual representation again to add and edit an entity. Thus, you might want to create a new entity even before filling out the visualization of the view for it. This would force update notifications to initiate an update on the controller of the selected results, i.e. an empty line will appear shortly before the MVC concept fully appears for adding or editing.
The second problem would be obvious if updates accumulated before saveContext became too extensive, and the save operation would take longer than 1 / 60th of a second. Since in this case the user interface would be blocked until the saving is completed, and you would have a significant transition, for example, when scrolling.
Both problems can be solved using several controlled objective contexts.
A “traditional” multi
-context approach Think of each managed objective context as a temporary block of change. Before the release of iOS 5, you probably heard about changes in other contexts and combined the changes from the moment of notification into the main context. A typical installation would look like this block diagram:

Create a temporary context to use for the background task queue. And save the changes there, set the same permanent storage coordinator in the temporary context, as in the main context. According to Marcus Sarr, it should look like this:
Despite the fact that the NSPersistentStoreCoordinator is not thread-safe, NSManagedObjectContext knows how to block it properly when it is in use. Therefore, we can attach as many NSManagedObjectContext objects to the NSPersistentStoreCoordinator as we want without fear of collision.
Calling saveContext in the background context will write the changes to the repository file and also initiate NSManagedObjectContextDidSaveNotification.
In code, it will look something like this:
Creating a temporary context is very fast, so you don’t have to worry about the frequent creation and release of these temporary contexts. The point is, in order to set persistentStoreCoordinator to the same main context so creation must also happen in the background.
I prefer this simplified installation of the CoreData stack:
Now consider the notification handler that we set so that the didSave notification pops up every time.
First, we want to avoid merging our own changes. Also, if we have several CoreData databases in the same application, we try to avoid merging the changes that are intended for another database. I encountered such a problem in one of my applications, that's why I check the coordinator of the persistent storage. Finally merge the changes using the mergeChangesFromContextDidSaveNotification method. The notification has a dictionary of all changes in its payload, and this method knows about integrating them into the context.
Passing managed objects between contexts
It is strictly forbidden to move the managed object that you received from one context to another. There is an easy way to deal with the “mirror” of a managed object through an ObjectID. This identifier is thread-safe, and you can always get it from one instance of NSManagedObject and then call objectWithID. The second context will then receive its own copy of the managed objects to work with it.
The described approach is fully backward compatible up to the first version of iOS, which received support for CoreData with iOS 3. If you need only iOS 5 support for your application, then there is a more modern approach, which we will consider below.
Parent / child context
In iOS 5, it is now possible for a managed object context to contain parentContext. Calling the saveContext method pushes changes from the child context to the parent without having to resort to a method that involves merging the contents of a dictionary describing the changes. At the same time, Apple added the ability for contexts to have their own separate queue for making changes both synchronously and asynchronously.
The type of concurrency of the queue is set in the new initializer initWithConcurrencyType on NSManagedObjectContext. Note that in this diagram, I have added several child contexts, so that everyone has the same main context queue as the parent.

Each time, when saving, the child context will save changes to its parent, and this leads to the fact that the controller of the selected results must also be aware of these changes. However, this still does not save the data, since the background context does not know about the persistent storage coordinator. To get data to disk, you need an additional saveContext method: on the main context queue.
The first necessary change for this approach is to change the main concurrency type context to NSMainQueueConcurrencyType. In the aforementioned _setupCoreDataStack, the initial line changes look like the following and there is no longer any need to receive merge notifications.
A lengthy background operation will look like this:
Each context now needs to use performBlock: (async) or performBlockAndWait: (sync) to work. This ensures that the operations contained in the block use the correct queue. In the above example, a long operation is performed on the background queue. As soon as everything is ready for you, and the changes are redirected to the parent through the saveContext method, then the asynchronous performBlock method will appear to save mainMOC. And it will again happen on the correct queue, as provided by performBlock.
Child contexts, unlike parents, are not updated automatically. You can download them again to receive updates, but in most cases they are temporary, and so we don’t have to worry about it. As long as the main context queue receives changes, the controllers of the selected results are updated, and we have persistence while maintaining the main context.
The amazing simplification provided by this approach is that you can create a temporary context (as a child) for any view visualization that has a Save and Cancel button. If you pass the managed object for editing, then you transfer it (via the objectID mentioned above) to the temporary context. The user has the ability to update all elements of the managed object. If he clicks Save, then the entire temporary context is saved. If he clicks on cancel, then nothing needs to be done, because the changes are discarded along with the temporary context.
You still do not spin your head from all this information? If not, here's aerobatics about CoreData multi-context.
Asynchronous data storage
Core Data gurus Marcus Zarra showed me the following approach, which is based on the aforementioned Parent / Child method, but adds extra context exclusively for writing to disk. As mentioned earlier, a long write operation could block the main thread for a short time by freezing the UI. In the framework of this reasonable approach, the recording is allocated in a separate queue, and the user interface maintains smooth operation (remains smooth, does not “freeze”).

The setup for CoreData is also quite simple. All we need to do is move the persistentStoreCoordinator into our new hidden context and make the main context a child.
Now you need to make three different saves for each update: the temporary context, the main UI context, and for writing to disk. But just as easily as before, you can implement the performBlocks stack ... The user interface remains unlocked for a long database operation (for example, importing a large number of records), as well as when it is written to disk.
Conclusion
iOS 5 has greatly simplified work with CoreData on background queues, and received changes coming from child contexts to their parents. If you are still using iOS 3/4 then all these features are not available to you. But if you are starting a new project that has iOS 5 as a minimum requirement, you can immediately create Marcus Sarah's Turbo Approach, as described above.
Zach Woldowski pointed out to me that using a hidden type of queue concurrency to “edit presentation visualization” might be redundant. If you use NSContainmentConcurrencyType instead of rendering the view of the child context then you do not need to perform a performBlock wrapper. All you need is a performBlock on mainMOC to save.
The type of concurrency constraint is the “old way” of executing contexts, but this does not mean that it was traditional. It simply binds context operations to a self-managing threading model. The set of turns of the hidden queue for each new controller is wasteful, unnecessary, and slow.-performBlock: and-performBlockAndWait: do not work with the type of restriction parallelism for the reason that neither blocks nor blocking are necessary when you create several contexts in the method.
NSManagedObjectContext knows how to save and merge intelligently, and therefore the main context of the stream is tied to the main stream, its merges are always performed safely. Editing the presentation visualization is associated with the main stream in the same way as the main presentation visualization; the only way is a separate operation, which is only in the UI, so it is suitable for using the type of restriction parallelism here. The editing context is conceptually not a “new” thing, it just postpones the change until later, while still allowing you to revert the changes completely.
Thus, it really comes down to your personal preference: a hidden queue with performBlock or without concurrency restrictions. As for me, I try to prefer hidden queues because of the security that I get from using them.
ps For many it may seem that the article is useless, but I hope that some will still bring out something useful from this article. Do not scold much for the translation, if there are comments, write in a personal, fix :)
When you start using CoreData to store data in your applications, you start working with a single managed objective context (MOC). This is what is used in the template when creating a project in xCode, if you check the box next to "Use Core Data" when creating a project.

Using CoreData in combination with NSFetchedResultsController greatly simplifies the work with any kind of list of items that are displayed on the screen in a table view.
There are two scenarios in which you would like to branch out, i.e. using several controlled objective contexts: 1) to simplify the process of adding / editing new elements and 2) to avoid blocking the UI. In this post I want to consider ways to create your contexts in order to get what you want.
First, let's look at setting a single context. You need a persistent store coordinator (PSC) to access the database file on disk. So that this coordinator understands how this database is structured, you need a model. This model is combined from all the model definitions contained in the project and indicates to CoreData about this database structure. The coordinator is installed on the managed context object through the function property. Remember the first rule: a managed objective context will be written to disk using the coordinator if you call saveContext.

Consider this scheme. Each time you insert, update or delete an entity in this single controlled objective context, the selected results controller will be notified of these changes and will update its table view content. This is independent of context preservation. You can save as rarely or as often as you want. The Apple template saves on every addition of an entity and also (not strange) on applicationWillTerminate.
This approach is mainly suitable for most basic cases, but as I said before, there are two problems with it. The first is related to adding a new entity. You probably want to use the same bby / visual representation again to add and edit an entity. Thus, you might want to create a new entity even before filling out the visualization of the view for it. This would force update notifications to initiate an update on the controller of the selected results, i.e. an empty line will appear shortly before the MVC concept fully appears for adding or editing.
The second problem would be obvious if updates accumulated before saveContext became too extensive, and the save operation would take longer than 1 / 60th of a second. Since in this case the user interface would be blocked until the saving is completed, and you would have a significant transition, for example, when scrolling.
Both problems can be solved using several controlled objective contexts.
A “traditional” multi
-context approach Think of each managed objective context as a temporary block of change. Before the release of iOS 5, you probably heard about changes in other contexts and combined the changes from the moment of notification into the main context. A typical installation would look like this block diagram:

Create a temporary context to use for the background task queue. And save the changes there, set the same permanent storage coordinator in the temporary context, as in the main context. According to Marcus Sarr, it should look like this:
Despite the fact that the NSPersistentStoreCoordinator is not thread-safe, NSManagedObjectContext knows how to block it properly when it is in use. Therefore, we can attach as many NSManagedObjectContext objects to the NSPersistentStoreCoordinator as we want without fear of collision.
Calling saveContext in the background context will write the changes to the repository file and also initiate NSManagedObjectContextDidSaveNotification.
In code, it will look something like this:
dispatch_async(_backgroundQueue, ^{
// create context for background
NSManagedObjectContext *tmpContext = [[NSManagedObjectContext alloc] init];
tmpContext.persistentStoreCoordinator = _persistentStoreCoordinator;
// something that takes long
NSError *error;
if (![tmpContext save:&error])
{
// handle error
}
});
Creating a temporary context is very fast, so you don’t have to worry about the frequent creation and release of these temporary contexts. The point is, in order to set persistentStoreCoordinator to the same main context so creation must also happen in the background.
I prefer this simplified installation of the CoreData stack:
- (void)_setupCoreDataStack
{
// setup managed object model
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Database" withExtension:@"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
// setup persistent store coordinator
NSURL *storeURL = [NSURL fileURLWithPath:[[NSString cachesPath] stringByAppendingPathComponent:@"Database.db"]];
NSError *error = nil;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:_managedObjectModel];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error])
{
// handle error
}
// create MOC
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:_persistentStoreCoordinator];
// subscribe to change notifications
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_mocDidSaveNotification:) name:NSManagedObjectContextDidSaveNotification object:nil];
}
Now consider the notification handler that we set so that the didSave notification pops up every time.
- (void)_mocDidSaveNotification:(NSNotification *)notification
{
NSManagedObjectContext *savedContext = [notification object];
// ignore change notifications for the main MOC
if (_managedObjectContext == savedContext)
{
return;
}
if (_managedObjectContext.persistentStoreCoordinator != savedContext.persistentStoreCoordinator)
{
// that's another database
return;
}
dispatch_sync(dispatch_get_main_queue(), ^{
[_managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
});
}
First, we want to avoid merging our own changes. Also, if we have several CoreData databases in the same application, we try to avoid merging the changes that are intended for another database. I encountered such a problem in one of my applications, that's why I check the coordinator of the persistent storage. Finally merge the changes using the mergeChangesFromContextDidSaveNotification method. The notification has a dictionary of all changes in its payload, and this method knows about integrating them into the context.
Passing managed objects between contexts
It is strictly forbidden to move the managed object that you received from one context to another. There is an easy way to deal with the “mirror” of a managed object through an ObjectID. This identifier is thread-safe, and you can always get it from one instance of NSManagedObject and then call objectWithID. The second context will then receive its own copy of the managed objects to work with it.
NSManagedObjectID *userID = user.objectID;
// make a temporary MOC
dispatch_async(_backgroundQueue, ^{
// create context for background
NSManagedObjectContext *tmpContext = [[NSManagedObjectContext alloc] init];
tmpContext.persistentStoreCoordinator = _persistentStoreCoordinator;
// user for background
TwitterUser *localUser = [tmpContext objectWithID:userID];
// background work
});
The described approach is fully backward compatible up to the first version of iOS, which received support for CoreData with iOS 3. If you need only iOS 5 support for your application, then there is a more modern approach, which we will consider below.
Parent / child context
In iOS 5, it is now possible for a managed object context to contain parentContext. Calling the saveContext method pushes changes from the child context to the parent without having to resort to a method that involves merging the contents of a dictionary describing the changes. At the same time, Apple added the ability for contexts to have their own separate queue for making changes both synchronously and asynchronously.
The type of concurrency of the queue is set in the new initializer initWithConcurrencyType on NSManagedObjectContext. Note that in this diagram, I have added several child contexts, so that everyone has the same main context queue as the parent.

Each time, when saving, the child context will save changes to its parent, and this leads to the fact that the controller of the selected results must also be aware of these changes. However, this still does not save the data, since the background context does not know about the persistent storage coordinator. To get data to disk, you need an additional saveContext method: on the main context queue.
The first necessary change for this approach is to change the main concurrency type context to NSMainQueueConcurrencyType. In the aforementioned _setupCoreDataStack, the initial line changes look like the following and there is no longer any need to receive merge notifications.
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_managedObjectContext setPersistentStoreCoordinator:_persistentStoreCoordinator];
A lengthy background operation will look like this:
NSMangedObjectContext *temporaryContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
temporaryContext.parentContext = mainMOC;
[temporaryContext performBlock:^{
// do something that takes some time asynchronously using the temp context
// push to parent
NSError *error;
if (![temporaryContext save:&error])
{
// handle error
}
// save parent to disk asynchronously
[mainMOC performBlock:^{
NSError *error;
if (![mainMOC save:&error])
{
// handle error
}
}];
}];
Each context now needs to use performBlock: (async) or performBlockAndWait: (sync) to work. This ensures that the operations contained in the block use the correct queue. In the above example, a long operation is performed on the background queue. As soon as everything is ready for you, and the changes are redirected to the parent through the saveContext method, then the asynchronous performBlock method will appear to save mainMOC. And it will again happen on the correct queue, as provided by performBlock.
Child contexts, unlike parents, are not updated automatically. You can download them again to receive updates, but in most cases they are temporary, and so we don’t have to worry about it. As long as the main context queue receives changes, the controllers of the selected results are updated, and we have persistence while maintaining the main context.
The amazing simplification provided by this approach is that you can create a temporary context (as a child) for any view visualization that has a Save and Cancel button. If you pass the managed object for editing, then you transfer it (via the objectID mentioned above) to the temporary context. The user has the ability to update all elements of the managed object. If he clicks Save, then the entire temporary context is saved. If he clicks on cancel, then nothing needs to be done, because the changes are discarded along with the temporary context.
You still do not spin your head from all this information? If not, here's aerobatics about CoreData multi-context.
Asynchronous data storage
Core Data gurus Marcus Zarra showed me the following approach, which is based on the aforementioned Parent / Child method, but adds extra context exclusively for writing to disk. As mentioned earlier, a long write operation could block the main thread for a short time by freezing the UI. In the framework of this reasonable approach, the recording is allocated in a separate queue, and the user interface maintains smooth operation (remains smooth, does not “freeze”).

The setup for CoreData is also quite simple. All we need to do is move the persistentStoreCoordinator into our new hidden context and make the main context a child.
// create writer MOC
_privateWriterContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_privateWriterContext setPersistentStoreCoordinator:_persistentStoreCoordinator];
// create main thread MOC
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
_managedObjectContext.parentContext = _privateWriterContext;
Now you need to make three different saves for each update: the temporary context, the main UI context, and for writing to disk. But just as easily as before, you can implement the performBlocks stack ... The user interface remains unlocked for a long database operation (for example, importing a large number of records), as well as when it is written to disk.
Conclusion
iOS 5 has greatly simplified work with CoreData on background queues, and received changes coming from child contexts to their parents. If you are still using iOS 3/4 then all these features are not available to you. But if you are starting a new project that has iOS 5 as a minimum requirement, you can immediately create Marcus Sarah's Turbo Approach, as described above.
Zach Woldowski pointed out to me that using a hidden type of queue concurrency to “edit presentation visualization” might be redundant. If you use NSContainmentConcurrencyType instead of rendering the view of the child context then you do not need to perform a performBlock wrapper. All you need is a performBlock on mainMOC to save.
The type of concurrency constraint is the “old way” of executing contexts, but this does not mean that it was traditional. It simply binds context operations to a self-managing threading model. The set of turns of the hidden queue for each new controller is wasteful, unnecessary, and slow.-performBlock: and-performBlockAndWait: do not work with the type of restriction parallelism for the reason that neither blocks nor blocking are necessary when you create several contexts in the method.
NSManagedObjectContext knows how to save and merge intelligently, and therefore the main context of the stream is tied to the main stream, its merges are always performed safely. Editing the presentation visualization is associated with the main stream in the same way as the main presentation visualization; the only way is a separate operation, which is only in the UI, so it is suitable for using the type of restriction parallelism here. The editing context is conceptually not a “new” thing, it just postpones the change until later, while still allowing you to revert the changes completely.
Thus, it really comes down to your personal preference: a hidden queue with performBlock or without concurrency restrictions. As for me, I try to prefer hidden queues because of the security that I get from using them.
ps For many it may seem that the article is useless, but I hope that some will still bring out something useful from this article. Do not scold much for the translation, if there are comments, write in a personal, fix :)