I have seen many times people use many managedObjectContext, but aside from when using the Undo manager, what is the real reason for using multipleManagedObjectContext? Why can it be useful to use more than one? Could you please show a few examples?
Managed object contexts are not thread safe so if you ever need to do any kind of background work with your Coredata objects (i.e. a long running import/export function without blocking the main UI) you will want to do that on a background thread.
In these cases you will need to create a new managed object context on the background thread, iterate through your coredata operation and then notify the main context of your changes.
You can find an example of how this could work here Core Data and threads / Grand Central Dispatch
Related
I am developing an application that uses Core Data for internal storage. This application has the following functionalities :
Synchronize data with a server by downloading and parsing a large XML file then save the entries with core data.
Allow user to make fetches (large data fetches) and CRUD operations.
I have read through a lot and a lot of documentation that there are several patterns to follow in order to apply multithreading with Core Data :
Nested contexts : this patterns seems to have many performance issues (children contexts block ancestor when making fetches).
Use one main thread context and background worker contexts.
Use a single context (main thread context) and apply multithreading with GCD.
I tried the 3 mentioned approaches and i realized that 2 last ones work fine. However i am not sure if these approaches are correct when talking about performance.
Is there please a well known performant pattern to apply in order to make a robust application that implements the mentioned functionalities ?
rokridi,
In my Twitter iOS apps, Retweever and #chat, I use a simple two MOC model. All database insertions and deletions take place on a private concurrent insertionMOC. The main MOC merges through -save: notifications from the insertionMOC and during merge processing emits a custom UI update notification. This lets me work in a staged fashion. All tweets come in to the app are processed on the background and are presented to the UI when everything is done.
If you download the apps, #chat's engine has been modernized and is more efficient and more isolated from the main thread than Retweever's engine.
Anon,
Andrew
Apple recommends using separate context for each thread.
The pattern recommended for concurrent programming with Core Data is
thread confinement: each thread must have its own entirely private
managed object context. There are two possible ways to adopt the
pattern: Create a separate managed object context for each thread
and share a single persistent store coordinator. This is the
typically-recommended approach. Create a separate managed object
context and persistent store coordinator for each thread. This
approach provides for greater concurrency at the expense of greater
complexity (particularly if you need to communicate changes between
different contexts) and increased memory usage.
See the apple Documentation
As per apple documentation use Thread Confinement to Support Concurrency
Creating one managed object context per thread. It will make your life easy. This is for when you are parsing large data in background and also fetching data on main thread to display in UI.
About the merging issue there are some best ways to do them.
Never pass objects between thread, but pass object ids to other thread if necessary and access them from that thread, for example when you are saving data by parsing xml you should save them on current thread moc and get the ids of them, and pass to UI thread, In UI thread re fetch them.
You can also register for notification and when one moc will change you will get notified by the user info dictionary which will have updated objects, you can pass them to merge context method call.
I've read through the materials regarding core data and threading and understand the principles of a separate MOC for each thread. My question is, what's the best way to dynamically determine whether to use a different MOC or the main one. I have some methods that are sometimes called on the main thread, sometimes in background. Is dynamically detecting thread not recommended or is it okay? Any pitfalls? Or do people just write separate methods for the background processes?
Some additional detail...i have a refresh process that performs a bunch of updates off the main thread (so not to lock the UI while user is waiting) using a simple performSelectorInBackground. This process moves thru steps serially so i dont have to worry about multiple things accessing DB on THIS thread, obviously the trick is keeping the main and background safe. I have implemented using a separate context and merging in other places, but i recently rearchitected and am now using methods in the background i wasnt before. So i wanted to rewrite those, use the separate context, but sometimes ill be hitting them on the main thread and can access main MOC just fine.
You do not give much detail about how you are managing your background operation and what you are doing with it, so it is pretty difficult to suggest anything.
In general, since creating a MOC is a pretty fast operation, you could create a new temporary MOC each time you need one in read-only mode (e.g. for data lookup). If you also have updates (e.g., adding new object or modifying existing ones), you should factor in the cost of merging, thus creating temporary MOCs each time could not be a good approach.
Another good approach could be creating a child context in your background thread.
But, as I said, it all depends on what you are doing.
Have a look at this good post about multi-threaded Core Data usage: Multi-Context CoreData. It describes a couple of scenarios and the solutions for them.
EDIT:
You could certainly use isMainThread to discriminate between the two cases (where you can use the main MOC and when you need a new one). That is what that method is for (and it is surely not expensive).
On the other hand, if you want a cleaner implementation, the best approach IMO would be creating a child MOC (which simplifies a lot the merging process - it becomes almost automatic, since you just need to save the parent context after saving the temporary context).
You'll need a new NSManagedObjectContext for each thread, and you'll need to create new versions of your NSManagedObjects from that thread's new MOC. Read #sergio's answer regarding the pros/cons of that approach.
To check if you're on the main thread, you can use [NSThread isMainThread] and make determinations that way. Or, when you're spinning up a new thread to crunch on CoreData, also create a new MOC.
A common approach is to associate each managed object context with a particular serial dispatch queue. So there's one for the main queue, and you can dynamically create them otherwise.
Once you're tying these things to queues, you can use dispatch_queue_set_specific to attach a particular context to a particular queue and dispatch_get_specific to get the context for the current queue. They both turned up in iOS 5 so you'll see some iOS 4-compatible code that jumps through much more complicated hoops but you don't really need to worry about it any more.
Alternatively, if your contexts are tired to particular NSRunLoops or NSThreads, store the context to [[NSThread currentThread] threadDictionary] — it's exactly what it's there for.
I'm working on an app that requires multiple asynchronous downloads and saving of their contents to Core Data entities. One of the downloads is large and noticed the UI was being blocked while creating/writing to the managed object context. My research led me to read up on concurrent Core Data setups and I started implementing one of these. But I'm running into issues and spending a lot of time correcting things.
Before I continue, I'm thinking about simply setting up a single MOC with NSPrivateQueueConcurrencyType. Nothing I read mentions doing this. This way I could optionally perform MOC operations in the background, or just use the main thread as usual while maintaining a single MOC.
Is this a good approach? If not, what is wrong with it? I doubt this is the right approach because if it is, NSPrivateQueueConcurrencyType dominates NSMainQueueConcurrencyType and there would be no reason to have the latter.
There is nothing wrong with using a NSPrivateQueueConcurrencyType MOC for background tasks.
But you will probably still need a NSMainQueueConcurrencyType MOC.
From the documentation:
The context is associated with the main queue, and as such is tied
into the application’s event loop, but it is otherwise similar to a
private queue-based context. You use this queue type for contexts
linked to controllers and UI objects that are required to be used only
on the main thread.
As an example, for a fetched results controller, you would use the
NSMainQueueConcurrencyType MOC.
Im trying to figure out right now all my core data is managed on the main thread, with a single context. I used instrument and noticed the save operations are blocking the main thread for quite some time. I would like to run my save operations on a background thread using GCD
Now i understand how to setup this process with creating a new thread / context and listen for "save" notifications to merge back on the main thread.
But when i create a new context all the changes from the main thread's context needs to be inserted / merged into the new, else it wont have anything to save?? i can seem to find any examples for this.
For example on my main context ive:
inserted 2x users
deleted 3 wallposts
updated some friend relationships.
now how do i make my background context know if these changes?
Would it make sense to create the background context right when the application starts and have it listen for NSManagedObjectContextObjectsDidChangeNotification on the main thread to constantly keep it in sync??
In iOS5 you can do this easily with new concurrency types. If you create a context with NSPrivateQueueConcurrencyType, you will not block the main thread. But you have to access the context inside your block passed in to managedObjectContext:performBlock method.
You can't pass NSManagedObject instances between threads. If you are struggling with large datasets you might want to read this post by Marcus Zarra.
I'm beginning to think the answer to my question is 'No', but I'm still confused and uncertain about this. So please confirm. I've already learned the need to be careful when using Core Data with multiple threads. NSManagedObjectContext objects must not cross thread boundaries. Being a newbie with both threads and Core Data, I happily found that GCD should make some of this easier.
Naively perhaps, I then thought I would simply create a dedicated GCD dispatch queue for dealing with Core Data (or even, if needed, have multiple dispatch queues each with its own core data context). That would have been simple.
But now I realize that one big advantage of GCD dispatch queues is that it manages and makes use of multiple threads as needed. So - if I understand this right - tasks I hand off to one and the same dispatch queue, could end up running in different threads, potentially handing off a core data context from one thread to another, and having things go wrong. Is that right?
I've read many related questions and answers, for example Core Data and threads / Grand Central Dispatch, but I remain somewhat confused. The accepted answer to that question, using GCD queues, does ensure that a new context is created on each thread, but does not point out the necessity of doing this. Another answer says "You could execute all CoreData work on a queue named com.yourcompany.appname.dataaccess" seeming to imply that as long as the Core Data work is confined to one GCD dispatch queue, then all is OK. Maybe it is not.
Update: As #adib points out in a comment, the approach to serialized managed object context (MOC) access has changed in iOS 9 and MacOS X 10.11. NSConfinementConcurrencyType, the thread confinement strategy, is now deprecated in favor of NSPrivateQueueConcurrencyType and NSMainQueueConcurrencyType. In other words, stop using threads for concurrent access to Core Data objects and instead start using GCD. You should use either the main dispatch queue or the one associated with the MOC, depending on how you configure the MOC, and not a queue of your own creation. This is easy to do using NSManagedObject's -performBlock: or -performBlockAndWait: methods.
Short answer: Using a serial dispatch queue can provide serialized access to a managed object context, and that's an acceptable way to implement the "thread confinement" strategy even though GCD may actually employ multiple threads.
Longer answer:
The accepted answer to that question, using GCD queues, does ensure
that a new context is created on each thread, but does not point out
the necessity of doing this.
The big thing you need to remember is that you must avoid modifying the managed object context from two different threads at the same time. That could put the context into an inconsistent state, and nothing good can come of that. So, the kind of dispatch queue that you use is important: a concurrent dispatch queue would allow multiple tasks to proceed simulaneously, and if they both use the same context you'll be in trouble. If you use a serial dispatch queue, on the other hand, two or more tasks might execute on different threads, but the tasks will be executed in order, and only one task will run at a time. This is very similar to running all the tasks on the same thread, at least as far as maintaining the context's consistency goes.
See this question and answer for a much more detailed explanation.
This is how Core Data has always worked. The Concurrency with Core Data section of the Core Data Programming Guide gives advice on how to proceed if you do decide to use a single context in multiple threads. It talks mainly about the need to be very careful to lock the context any time you access it. The point of all that locking, though, is to ensure that two or more threads don't try to use the context simultaneously. Using a serialized dispatch queue achieves the same goal: because only one task in the queue executes at a time, there's no chance that two or more tasks will try to use the context at the same time.
AFAIK you're correct; GCD doesn't make guarantees about the thread in which the queue is run. Blocks and function calls sent to the queue will be run one at a time, but if Core Data does something with the current thread, e.g. installs a run loop source or observer, things probably won't work as intended.
However, on Mac OS X 10.7, NSManagedObjectContext can be set to run on the main thread, on a separate thread, or in a private queue.