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
Related
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.
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.
Having some truly strange behavior with Core Data on iOS.
I have a NSManagedObjectContext for my main thread used to read data from a SQLLite persistent store and display it to the user. I also have background processes managed by an NSOperationQueue. These background processes create an NSMangedObjectContext, fetch data from remote servers, and persist that data to the local Core Data store.
I have registered for NSManagedObjectContextDidSaveNotification and when I receive those notifications I call mergeChangesFromContextDidSaveNotification on the main thread's NSManagedObjectContext (passing the notification object as an argument).
This is all very standard and the way that all the Core Data document suggest that you deal with multi-threading.
Up until recently, I have always been inserting new objects into the data store and not modifying objects in the data store. This worked fine. After a background thread writes new data, the merge occurs, I send a notification to the UIController and redraw my display. The display draws correctly.
Recently I have made a change, and the background thread is both inserting and modifying objects. But all the rest of the pattern remains the same. Now after the merge, the data in my main thread’s NSManagedObjectContext is corrupted. If I try to query for objects, I get nothing back. If I try to examine objects which I already have a reference to all their relationships are nil (not faults but nil). I have checked the SQLLite database and the data is all there.
The only solution seems to be to reset the NSManagedObjectContext which isn’t acceptable given the architecture of the applicaiton.
Ok, a final bit of strangeness. If my background thread is only updating attributes (primitves) then I don’t get this strange behavior. However, if I update the relationships themselves, then I get these empty fetch request results and nil’d out relationships.
What am I missing?
Ok, I figured out my problem.
My problem is the way that mergeChangesFromContextDidSaveNotification merges changes from one thread to another. As I have read several times, it does not do a playback of the changes you made but rather merges the final state together. I didn't realize the ramifications of this. In my background thread, I am deleting an object, but before I delete it I nil out some of its relationships because they are marked as cascade delete and I don't want them deleted.
Apparently when I merge those deleted objects into the main context, Core Data still enforces the delete rules and cascade deletes the related objects in the main context.
I changed my delete rules and now my relationships is not getting incorrectly nil'd out. It is unfortunate that I have a relationship which in some cases I want cascade deleted and in others I don't. I will have to work on that.
I am trying to save data into core data in the background thread, as it takes quite some time to save.
I did:
[self performSelectorInBackGround:#selector(insertRecord:) withObject:data];
When all works fine until the line in insertRecord Method hits contextsave:&error. Program received signal : "SIGABRT"
Am I doing anything wrong? it works ok when its in main thread, I just move the codes to another method and run it in background and it doesn't work anymore.
According to the "Concurrency with Core Data" section of Core Data Programming Guide:
The pattern recommended for concurrent programming with Core Data is
thread confinement: each thread must have its own entirely private
managed object context.
and
Using thread confinement, you should not pass managed objects or
managed object contexts between threads.
It looks like you're passing a managed object to the background thread, which is forbidden. I don't know if you're also trying to share your managed object context between threads.
That document describes a couple of workarounds for passing managed objects to other threads. You'll need to implement one of them.
The problem here is that managed object contexts aren't thread-safe. If your -insertRecord: method uses the main thread's managed object context, you're asking for trouble.
The blog Cocoa Is My Girlfriend has an article, Core Data and Threads, Without the Headache on this very topic and suggests some strategies for saving in the background. The basic idea is to make changes on a context that belongs to a background thread, and then merge the changes into the main thread's context. That gives you an up-to-date context that you can save in the background while still keeping the main thread's context current.
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/