Mastering In CoreData (Part 14 Multithreading Concurrency Strategy Parent — Child Context)
Parent/Child Managed Object Context
As shown in Figure 1 , Since iOS 6, there’s an even better, more elegant strategy. The concept behind parent/child managed object contexts is that a child managed object context is dependent on its parent managed object context for saving its changes to the corresponding persistent store. In fact, a child managed object context doesn’t have access to a persistent store coordinator.
Whenever a child managed object context is saved, the changes it contains are pushed to the parent managed object context. There’s no need to use notifications to manually merge the changes into the main or parent managed object context.Managed object contexts can be nested. A child managed object context can have a child managed object context of its own. The same rules apply. This approach is recommended by Apply
The context connected to the persistent store coordinator should be the single source of truth which means that it should be created on Main thread, whose responsibility is to tackle UI related tasks.
How to Achieve Concurrency
You can see the simple concurrency architecture as shown in Figure 2. There are few points you should follow
- Child context is responsible for doing heavy tasks which is using some background thread
- Main thread is not blocked by this heavy task since child context is using background thread.
- Parent
ManagedObjectContext
should be created onNSMainQueueConcurrencyType
- Child
ManagedObjectContext
is created onNSPrivateQueueConcurrencyType
- When Child
ManagedObjectContext
are done with the heavy task Core data automatically merged changes to parent as well we just need to callsave
method on Child only - Child
ManagedObjectContext
can also have other child context in it
Recap of previous Part With This Strategy
Creating a child managed object context is slightly different from what we’ve seen so far. A child managed object context uses a different initializer, initWithConcurrencyType:. The concurrency type the initializer accepts defines the managed object context’s threading model.
NSMainQueueConcurrencyType
→ The managed object context is only accessible from the main thread. An exception is thrown if you try to access it from any other thread.
NSPrivateQueueConcurrencyTypea
→ When creating a managed object context with a concurrency type of NSPrivateQueueConcurrencyType
, the managed object context is associated with a private queue and it can only be accessed from that private queue.
There are two key methods that were added to the Core Data framework when Apple introduced parent/child managed object
contexts, perform:
and performAndWait:
Both methods will make your life much easier. When you call perform:
on a managed object context and pass in a block of code to execute, Core Data makes sure that the block is executed on the correct thread. In the case of the NSPrivateQueueConcurrencyType
concurrency type, this means that the block is executed on the private queue of that managed object context
Data Flow
As you can see in Figure 3. User List screen which displays all the users in the screen . It fetches users data from the parent context which is on main thread. Since it’s solely purpose is to interact with the UI so this context can’t do any heavy processing task. As shown in Figure 3 currently there are only 4 users in the database
Now UserListScreenViewController
needs data from server. It hits network call and the response comes with new 996 users. It will do parsing on that thread and will not affects main thread . User still can interact with the UI since we are not blocking main thread. Now after doing some heavy task it needs to merge these changes to parent as compared to previous notification strategy it can be done by calling only save method .
By calling save
method on child context all changes will merged into the parent context and these changes will not go to the persistent store until main context will also call save. So save method can push changes to one level up.
As you can see in the Figure 5 when child context completed its heavy processing task it merged changes to parent by calling save method only and parent will update the UI with the updated values. As you can see in the figure 5 there are number of things going on
- When child context finished all the processing it merged all the changes to main context by calling
save
method. After calling save method all changes will merged into the parent context. This merging will be done on main thread so merging time which is in milliseconds and in that time our main thread will be busy doing this . We moved the processing and parsing time on background thread only - When main context got the changes. Now refresh the UI will be done by the
UserListScreenViewController
not by main context responsibility when it asks to fetch data it will return updated data - At that point persistent store is not aware of the new users object we need to call
save
method on main context as well after that these changes will pushed to the persistent store as well. So we need to call twosave
method to push changes to persistent store. So using so many child context you need to think very carefully. Most of the application needs one child context which can do the majority of the concurrency tasks
Note : don’t make child context as a singleton if you need concurrency always create fresh one . Otherwise it will create a problem some times
Proof By coding
Let’s delete the application first. As shown in Figure 6 we added four Users to persistent store.
In the Figure 7 we proved it using code. To accomplished this we performed number of tasks
- Get the reference of main thread context using
persistentContainer
viewContext
property - Created Private Managed Object Context using init method in
NSMangedObjectContext
and then we made as a child of main context using contextparent
property as shown in Figure 7. So main context acts as a parent of private context . One thing you will see we didn’t usenewBackgroundContext,
when creating private context . We will discuss that in later . - Since we previously stored 4 Users object in Persistent Store so both fresh context should be print 4 users object and have same state as a persistent store as you can see in the logs as well
- On
Child Managed Object Context
(Private Context ) we created 996 users object that came from server. Main thread context still unaware of those objects in the background thread and main thread still performing and doing user interaction work since we didn’t block it - Now if we print users on both context only private printed 1000 users objects while main context still printed 4 since it is unaware of these changed happened in child context
- When we call
save
method onChild Managed Object Context
(Private Context ), it will automatically merged all the users object onParent Object Context
(Main Thread Context ) - As you can see both context
Child Managed Object Context and Parent Object Context
now have same state. While persistent store still contains 4 Users object. If you terminate app and print objects it will print 4 users objects since main context didn’t call save method yet.
Note : newBackgroundContext if you used while creating private context in parent child architecture application will crash. We will discuss this in later part
What Next?
In the next part we will look how to solve the Real application Concurrency Problem using Parent Child Concurrency Strategy that Core data Provides
Useful Links
https://code.tutsplus.com/tutorials/core-data-from-scratch-concurrency--cms-22131
https://www.raywenderlich.com/7586-multiple-managed-object-contexts-with-core-data-tutorial