Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Core Data: Parent context and change propagation

Tags:

ios

core-data

I have the following Core Data setup in my app:

Persistent Store Coordinator
  ^ Background MOC (NSPrivateQueueConcurrencyType)
      ^ Main Queue MOC (NSMainQueueConcurrencyType)

Here is the initialization code:

_backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_backgroundContext setPersistentStoreCoordinator:self.coordinator];
_mainContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_mainContext setParentContext:_backgroundContext];

I use the background MOC for importing large amounts of data. I also use it to perform complex fetch requests in the background and then pass the object IDs to the main queue to fetch the objects using these IDs.

This works quite well. However, I am not sure how to let the main queue MOC know about the changes made in the background MOC. I know that if I perform a fetch request on the main queue MOC, it will get the changes, but that's not what I want.

Is it OK to use the NSManagedObjectContextObjectsDidChangeNotification notification posted by the background MOC and call mergeChangesFromContextDidSaveNotification: on the main queue MOC? This should then cause the NSManagedObjectContextObjectsDidChangeNotification notification of the main queue MOC to fire. I am listening for this notification in my view controllers and examine the userInfo for changes and redisplay data accordingly. I think you usually do it this way if you have one persistent store coordinator with two attached MOCs. But I am not sure if it is the right way to do, when you have child/parent contexts.

like image 849
Florian Avatar asked Oct 18 '13 14:10

Florian


People also ask

What is a Core Data context?

A context consists of a group of related model objects that represent an internally consistent view of one or more persistent stores. Changes to managed objects remain in memory in the associated context until Core Data saves that context to one or more persistent stores.

When should I use Core Data?

Use Core Data to save your application's permanent data for offline use, to cache temporary data, and to add undo functionality to your app on a single device. To sync data across multiple devices in a single iCloud account, Core Data automatically mirrors your schema to a CloudKit container.

Can we have multiple managed object context in Core Data?

Most apps need just a single managed object context. The default configuration in most Core Data apps is a single managed object context associated with the main queue. Multiple managed object contexts make your apps harder to debug; it's not something you'd use in every app, in every situation.

What is NSManagedObject in Core Data?

In some respects, an NSManagedObject acts like a dictionary—it's a generic container object that provides efficient storage for the properties defined by its associated NSEntityDescription instance.


2 Answers

Having the main MOC use a private parent MOC for asynchronous I/O is fine. However, you should not use that parent MOC for anything but performing background work on behalf of the main MOC. There are many reasons for this (among them performance and nasty issues related to transient object IDs).

If you want to do background updating of the store, here is what I suggest.

PSC <--+-- PrivateMOC <---- MainMOC
       |
       +-- BackgroundPrivateMOC

This will allow background operation that causes the least interruption to the main MOC, while allowing the PSC caches to be shared.

Now, for sharing data...

The MainMOC should listen for and merge DidSave notifications from the BackgroundPrivateMO.

The BackgroundMOC can listen for and merge DidSave notifications from the PrivateMOC.

This allows merging to use only permanent object IDs and optimizes performance.

like image 82
Jody Hagins Avatar answered Nov 03 '22 01:11

Jody Hagins


I'd say that listening to NSManagedObjectContextObjectsDidChangeNotification notification is not probably the best solution.

The way I do it and it works is following. Here is main context creation:

_mainContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
_mainContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
_mainContext.persistentStoreCoordinator = _persistentStoreCoordinator;

Here is background context creation:

_backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
_backgroundContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
_backgroundContext.parentContext = self.mainContext;

Now, background context is only for writing (or reading) objects (may be in background thread). Main context is only for reading from the main queue. Save on background context should look like:

__block BOOL saved = [_backgroundContext save:error];
if (saved && _backgroundContext.parentContext) {
    [_backgroundContext.parentContext performBlockAndWait:^{
        saved = [self.parentContext save:error];
    }];
}

This save method guarantees that all changes will be propagated to main context. If you do a lot of work in many background threads get more familiar with performBlockAndWait: method, which provides mutual exclusion on context.

If you want to be notified about objects' changes, you don't have to listen for notification, you can simply setup NSFetchedResultsController and register as its delegate.

like image 43
Maciej Oczko Avatar answered Nov 03 '22 02:11

Maciej Oczko