Concurrency in CoreData with multiple contexts - ios

There are few ways of handling concurrency in CoreData
One of them is using parent/child managedObjectContexts like so:
let mainContext = NSManagedObjectContext( concurrencyType: .MainQueueConcurrencyType)
let childContext = NSManagedObjectContext( concurrencyType: .PrivateQueueConcurrencyType)
childContext.parentContext = mainContext
Another approach would be to have both the main and child contexts use the same persistentStoreCoordinator like so:
let mainContext = NSManagedObjectContext( concurrencyType: .MainQueueConcurrencyType)
let childContext = NSManagedObjectContext( concurrencyType: .PrivateQueueConcurrencyType)
childContext.persistentStoreCoordinator = mainContext.persistentStoreCoordinator
Since we'll need to use performBlock on the childContext and later save or execute fetch or anything on the mainContext, what would be the difference between these two approaches?
I read on Florian Kugler's blog that the former approach processes on the Main Thread (which I tried and it didn't) and the latter is the preferred way. But every other site that I've looked seem to prefer former parent/child context.
To make things more confusing, on RayWenderLich's CoreData book (chapter 10 for references), they've used both of the approaches without explaining why.

Ideally there are 3 simple rule to achieve concurrency in single persistentStore core data application.
only one Managed Object context (MOC) should attached with persistentStoreCoordinator is basic rule to avoid MOC lock, unlock on CURD operation.
Every MOC should attached with one thread like Main thread MOC, background thread MOC.
You can not pass the managed object from one MOC(thread) to other by MOC(thread), in this case just pass ObjectID.
To achieve all three rule, apple introduce parent- child MOC approach. There are so many combination on various post,
But each stack (parent- child MOC approach) is highly depend on the application data usability.
I have implemented as my main MOC contexts use persistentStoreCoordinator, And created background thread child MOC for data synchronisation,
and more local child MOC for each View controller for creating new records screen. this will work well for me and tested with 3500 records insertion on DB.
The benefit doing this I am getting the updated server sync data through my main MOC.
I know few critics for approach, that I will block the main thread, But you can utilise batch update, delete, asynchronous fetch request to minimise it.

Related

Core Data Multiple ManagedObjectContext

How the multiple ManagedObjectContext (MOC) works in core data(Swift 2, iOS 9). I have been through lots of links & material online and answers on StackOverflow, but couldn't find exact answer.
I want to know, suppose I have created main MOC which points to PersistentStoreCoordinator (PSC)and another private queue MOC which has parent context set to above mentioned main MOC.
Question 1: Then How this works ? Is the hierarchy built like this : Private queue MOC --> Main queue MOC --> PSC.
Question 2: If I call save on 'private queue MOC', will it save to main MOC and in turn automatically main MOC will save to PSC ? or After save on Private MOC we have to call explicit save on Main MOC to save it to PSC ?
I just started working on core data and online links are not so clear. So, any simplified explanation will be much appreciated.
Thanks!
For question 1, the hierarchy is however you create it. If you create a context with no parent, and then another one whose parent context is the first context, it'll be as you describe.
For question 2 I'll turn to the documentation on NSManagedObjectContext:
When you save changes in a context, the changes are only committed “one store up.” If you save a child context, changes are pushed to its parent. Changes are not saved to the persistent store until the root context is saved.
Saving is never automatic, so the parent context doesn't save changes until you tell it to save.

CoreData: Can a fetch on a context with NSPrivateQueueConcurrencyType block the main thread?

Sorry, this feels like a pretty basic conceptual question, but I am having a hell of a time finding the answer.
Suppose childContextis an NSManagedObjectContext with NSPrivateQueueConcurrencyType, and parentContext is an NSManagedObjectContext with NSMainQueueConcurrencyType. And suppose that childContext.parent == parentContext.
I know that saving into the childContext will eventually have to write to the parentContext, temporarily blocking the main thread; is the same true for fetching from the childContext? That is, would the performance of fetching from the child context differ in any way from fetching from the parent context?
Thanks!

Core Data Concurrency with parent/child relationship and NSInvocationOperation done right?

I've been using Core Data to store iOS app data in a SQLite DB. Data is also synchronized to a central server during login or after n minutes have passed and as the app grew, synchronization got slower and needed a background process to prevent freezing of the main thread.
For this I rewrote my Synchronization class to use NSInvocationOperation class for each process in the synchronization method. I'm using dependencies as the data being imported/synchronized has several dependent relationships.
Then I add all operations into an NSOperationQueue and let it run.
There are two additional important elements:
When I generate the operations to execute I also create a new UUID NSString, which represents a synchronization ID. This is used to track which Entities in Core Data have been synchronized, therefore, every Entity has an attribute that stores this synchronization ID. It is only used during the synchronization process and is accesses by each method passed to NSInvocationOperation.
The import and synchronization methods are the actual methods that access Core Data. Inside each of those methods I create a new instance of NSManagedObjectContext as follows:
PHAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *privateContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[privateContext setParentContext:[appDelegate mainManagedObjectContext]];
using NSPrivateQueueConcurrencyType as opposed to NSMainQueueConcurrencyType which is used in the AppDelegate for the main MOC. Then I create the parent/child relationship using the MOC from the main thread and that's that. When saving the child MOC, I understand that changes propagate back to the main MOC and to the store (SQLite).
I'm fairly new to concurrency in iOS and used this to guide me: http://code.tutsplus.com/tutorials/core-data-from-scratch-concurrency--cms-22131 (see Strategy #2). Different from this article and many other sources that I've seen, I do not subclass NSOperation. Instead, my approach uses NSInvocationOperation in order to pass each method that I want to run concurrently.
My question: Can you take a look at my code and see if you find something that I'm doing wrong or do you think that this will work? So far, I've had no problems running the code. But concurrency is tough to debug, so before we ship this code, I'd like to have more experienced eyes on this.
Thanks in advance!
Synchronization class code on pastebin.com: http://pastebin.com/CUzWw4Tv
(not everything is in the code paste, but it should be enough to evaluate the process, let me know if you need more)

Strange parent / child NSManagedObjectContext phenomenon

I have created two context like this:
// create writer MOC
_privateWriterContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_privateWriterContext setPersistentStoreCoordinator:_persistentStoreCoordinator];
// create main thread MOC
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
_managedObjectContext.parentContext = _privateWriterContext;
I have a NSFetchResultedController initiated with _managedObjectContext.
I know it is strange, but I am adding a record to the parent to _privateWriterContext, I am saving it.
What is surprising is that child context and so FRC gets notified about this event. Why? I have not reset-ed child, or anything else. I thought they are independent entities as long as child context will not get saved.
In #pteofil article I found this line:
When a change is made in a context, but not saved, it is visible to all of its’ descendants but not to its’ ancestors.
.. it is pushed to the persistent store (via the persistent store coordinator) and becomes visible to all contexts connected to the store.
This is not supposed to happen. Adding an NSManagedObject ('record' ) to the parentContext, will not make the child aware of this object automatically. Only when you make the childContext execute a fetch, then it will fetch from the parentContext.
To figure out what is going on in your case, here are some suggestions:
figure out when a fetch is executed on the childContext (this is done by the fetchedRestultsController when you set it up. Check if that fetch happens before or after you add the managedObject to the parentContext).
set breakpoints in all four delegate callbacks of the fetchedResultsController to find out for which object it is calling the methods (and see if it is the object you just added to the parentContext).
make absolutely sure you know which context you are sending messages too.
I have a used a similar approach, but different: the childContext is the context is used to parse in new data (on a private queue), and when this parsing is done, the chid calls save:. This will save the changes up to the parent, which is in my case the mainQueueContext. This call to save: will cause the mainQueueContext to receive all the newly parsed objects, and any fetchedResultsController using that mainQueueContext will then call it's delegate methods for the new/changed/updated/delete objects.
You could try inverting your child/parent relationship too and see if it works as described in the docs, just to find out whats going on.
I strongly recommend avoiding parent-child context setups. Our book goes into detail why they often lead to strange behaviour: https://www.objc.io/books/core-data/
Short story: They're not as independent as you might think.
Use multiple context that share a single persistent store coordinator if you need more than a single context.

Is it valid to mix the "1 moc per thread" strategy with "child moc" strategy?

Until now I always used a "main moc" for the main thread, initialised like this:
[[NSManagedObjectContext alloc] init];
and then I have NSOperation subclasses with their own moc that import data from the webservice, and I merge in the "main" moc on save observing NSManagedObjectContextDidSaveNotification
But now I need the ability to add "temporary" objects that the user can commit (or not) later. So it looks like a child context is the perfect fit, and in order to use child context I changed the initialization of my "main moc" to
[[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
The question is: can my current structure with NSOperation subclasses with their own moc (initialised without a type in their own thread) have problems if used along with the child context strategy? I don't think so, but I don't find much about mixing those strategies.
Note that I want to maintain the NSOperation subclasses and I don't want to use child contexts also for importing my data, because it suffers on performances, see http://floriankugler.com/blog/2013/4/29/concurrent-core-data-stack-performance-shootout
Moreover, when I create a new child of my main thread (that is of type NSMainQueueConcurrencyType), can I create it that child with type NSMainQueueConcurrencyType, continuing to work with my objects in the main thread as usual? Or am I forced to use NSPrivateQueueConcurrencyType, and use performBlock: for every operation on my objects?
I'm asking because is not clear from the documentation if using 2 moc on the same thread (the main thread in this case) could be a problem.
UPDATE:
Finally I implemented and used this solution on production and there are not problems so far. The only thing that I needed to do is to avoid merging on my NSManagedObjectContextDidSaveNotification notification when the moc has a parentContext (we don't want to merge mocs with a parent context, because they manage the merge themselves, but obviously the notification is triggered also for save on this kind of moc)
Yes, you can have multiple main queue context mocs, for exactly the reason you say - you create a temporary editing context for editing data which is then saved or discarded depending on user action.
As for mixing and matching with your operation queue contexts - that shouldn't be a problem. If you're merging back to the parent context, then any child contexts will pick up that data the next time they fetch.
Actually, that's indicated when working with multiple threads. Here, I've wrote an article exactly about this. The slave mocs mentioned in it, are designed exactly for working with operations, each operations on it's own slave moc.

Resources