Using fetch requests on background threads with Core Data - iphone

I want to use one ManagedObjectContext for the main thread and another, separate one for the background thread using NSOperation, as Apple suggests. And, each ManagedObjectContext shares the same persistent store.
Fetching could happen on the main thread because I use Core Data to populate a table view.
In the background, I need to access an NSManagedObject property that stores the name of an image. Then, the background thread will create and cache these images, which is the main reason for having the background thread.
Given such, is there any danger (like locking) if both threads attempt to access the persistent store because both could be fetching data from it the same time?

Each thread requires its own managed object context, but all threads need to share a single persistent store coordinator - that will take care of potential issues you're describing. See additional information in the Core Data Concurrency Programming Guide.

Two different MOCs can access the same PSC at the same time for reads.
However for writes, you need to lock and unlock your persistent store coordinator if there is a chance of concurrent writes.

As long as each thread uses its own NSManagedObjectContext, it's perfectly safe to have them share an NSPersistentStoreCoordinator. NSManagedObjectContext will deal with doing all the appropriate locking of the persistent store as needed. However, you do have to be careful not to share NSManagedObjects between threads.

Related

Core Data - sharing NSManagedObjects among multiple threads

I suffered all the consequences of using a single MOC in multiple threads - my app crashes at random points because the MOC is created in the main thread and I also use it to fill the DB in another thread.
Though the code is synchronized (#synchronize) using a global singleton the app crashes.
I read that using a separate MOC for each thread will make things ok but I also read that it is considered also a bad approach to share NSManagedObjects across threads.
My use case is the following:
1)I load and parse XML from a server and during the parsing I insert each new NSManagedObject in the database. This all happens in a separate thread.
2)From the main thread the user interacts with the UI which reads data from the database.
In both threads I use NSManagedObjects. How would you suggest me to fix this? I failed multiple times already.
Most often the app creashed with error suggesting that I am modifying a collection while enumerating it which is not true as the code is synchronized and while I am iterating it no modifying happens and vice versa - while I modify it I don't iterate and I save once I am done.
Use one NSManagedObjectContext per thread. If you communicate between threads, pass the NSManagedObjectID, which is thread safe, and fetch the object again from you thread context. In my apps I sometimes even use one context per controller.
To manage the different contexts, register an Observer for the NSManagedObjectContextDidChangeNotification. Within this notification handling, you pass the notification to each of your contexts via the mergeChangesFromContextDidSaveNotification: method. This method is thread save and makes the context update its state.
After this you have to refresh your views. If you have a table view based application, have a look at NSFetchedResultsController. This helps you update the table automatically with appropriate animations. If you don't use table views, you have to implement the UI update yourself.
If you are only supporting iOS 5 and above you don't need to deal with NSManagedObjectID and merging contexts anymore. You can use the new concurrency types of NSManagedObjectContext instead. Then do your operations within managedObjectContext:performBlock and they will be merged automatically.
See the answer from svena here for more information:
Core Data and Concurrency using NSOperationQueues

Core Data: fetch background and using objectWithID on main thread, performance benefit?

I read Core Data Programming Guide recently and Apple suggest us to do so
You fetch in one managed object context on a background thread, and pass the object IDs of the fetched
objects to another thread. In the second thread (typically the application's main thread, so that you can
then display the results), you use the second context to fault in objects with those object IDs (you use
objectWithID: to instantiate the object). (This technique is only useful if you are using an SQLite store, > since data from binary and XML stores is read into memory immediately on open.)
To my understanding, fetch on background context will not register the managed object in main thread context, so the managed object returned from objectWithID is most likely a fault. When we using it on main thread, we will trigger a new round of trip to the SQLite Store. So the UI maybe blocked.
Did I miss anything ? Are there a way to avoid I/O overhead on main thread ?
There's not much overheard when you do a fetch in the background and then use the objectID to do a fetch on the main thread. First, the record will be on the CoreData cache, which makes the same fetch on the main thread faster, and second, fetching with an objectID is much much much faster than fetching with your average fetch request. What you usually do is that you would create a background fetch request, find the objectID of the objects you are looking for, and move those objectIDs to the main thread. Of course, for the background thread, you have to use a different NSManagedObjectContext instance than the main thread one.
I would recommend you to check the WWDC 2010 video "Mastering Core Data". It goes into core data and multithreading, explaining the performance of caching and fetching on the background/main thread.

What happens when you try to save managedObjectContext from a different thread?

So I know that NSManagedObjects are not thread safe and managedObjectIDs are, and we need a separate managedObjectContext per thread. But recently I had an issue when I was doing some core data changes in the background (had a separate runloop thread for this) and performSelectorOnThread: method sometimes was simply not invoked on this runloop thread. It turned out that the reason was that I was doing
[someObject.managedObjectContext save:&error]
on this runloop thread and "someObject" was created on the main thread. But it would only "hung" runloop thread once in a while. So the question is what really happens if you try to save context in a different thread. I'm just looking for a deeper understanding, thanks.
From https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/CoreDataFramework/Classes/NSManagedObjectContext_Class/NSManagedObjectContext.html :
Core Data uses thread (or serialized queue) confinement to protect
managed objects and managed object contexts (see “Concurrency with
Core Data”). A consequence of this is that a context assumes the
default owner is the thread or queue that allocated it—this is
determined by the thread that calls its init method. You should not,
therefore, initialize a context on one thread then pass it to a
different thread. Instead, you should pass a reference to a persistent
store coordinator and have the receiving thread/queue create a new
context derived from that.
You will crash. Maybe it will work sometimes and you won't see a crash while debugging, but you should never do this. Object contexts and the managed objects in them should only be used on the thread on which they were created. Apple's documentation is very clear about this and gives many examples of how to handle situations where you may have long running operations (slow fetches or async saves). You should read the docs that talk about threading with Core Data for more info.

Saving NSManagedObjectContext in background

Is there a way to save my NSManagedObjectContext in the background, off of the main thread? The save slows down how the app is performing, as it routinely takes about 2 seconds.
Yes, there is.
Apple recommends using one context per thread to achieve that.
You can also use GCD for that, but you need to make sure that queues do not share context and you will also need to pass object ID, not the objects themselves between the queues or threads.
See this blog entry for detailed instructions: http://www.cimgf.com/2011/05/04/core-data-and-threads-without-the-headache/

Core Data with only one Data Context. Is it right?

I'm trying to make my first application using Objective C + Core Data, but I'm not sure it's the correct way, as it feels really weird to me.
I have only one data context, which I create at launch time, in the Application Delegate. This data context is used for all the operations (read, write). In another environment (C# and LINQ for example), I try to make these operations as unitary as possible. Here it seems I just have to create the data context once, and work with it without closing it ever (except when the application exits).
I also have an asynchronous operation in which I update this data. Of course, it uses the same data context again. It works, but doesn't feel right.
My Application Delegate keeps a NSArray of the objects contained in Core Data. I use this same NSArray in all my views.
I would actually naturally close the data context once I got all the objects I require, but... aren't the objects always attached to the data context? If I close or release the data context, all these objects will get releases as well, right?
As you can notice, there is something I'm missing here :) Thanks for your help.
The NSManagedObjectContext to which you refer is more of a "scratchpad" than a database connection. Objects are created, amended, destroyed in this working area, and only persisted ("written to the database" if you prefer) when you tell the MOC to save state. You can (and should) init and release MOCs if you are working in separate threads, but the App Delegate makes a MOC available so that all code executing on the main thread can use the same context. This is both convenient, and saves you from having to ensure that multiple MOCs are kept in sync with each other.
By keeping an NSArray of Core Data objects, you are in effect duplicating its functionality. Is there any reason for not working with an NSSet of Core Data objects provided by the MOC?
If you are working asynchronously, then you should not be sharing an NSManagedObjectContext object across threads, as they are not thread-safe. Instead, create one for each thread, but set them to use same NSPersistentStoreCoordinator. This will serialise their access to the persisted data, but you'll need to use notifications to make them each aware of the others changes.
There is a good tutorial/description on how to use Core Data on multiple threads here:
http://www.duckrowing.com/2010/03/11/using-core-data-on-multiple-threads/
1) CORE DATA AND THREADS, WITHOUT THE HEADACHE
http://www.cimgf.com/2011/05/04/core-data-and-threads-without-the-headache/
2) Concurrency with Core Data
http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/CoreData/Articles/cdConcurrency.html
3) Multi-Context CoreData
http://www.cocoanetics.com/2012/07/multi-context-coredata/