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.
To use Core Data in a multithreaded environment, ensure that: Managed object contexts are bound to the thread (queue) that they are associated with upon initialization. Managed objects retrieved from a context are bound to the same queue that the context is bound to.
If you need to pass a managed object from one thread to another, you use a managed object's objectID property. The objectID property is of type NSManagedObjectID and uniquely identifies a record in the persistent store.
There are multiple ways to use Core Data and a NSManagedObjectContext on a background thread. One way is the performBackgroundTask(_:) method on an NSPersistentContainer : // Spawns a new thread and creates a background // managed object context to perform operations // in the background persistentContainer.
An object space to manipulate and track changes to managed objects.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With