“Collection was mutated while being enumerated” during CoreData operation on background thread
Posted: | Author: Jörn | Filed under: iOS, Objective-C | Tags: Background, CoreData, Threads | 7 Comments »During my current project I had to move a time consuming CoreData operation to a background thread to avoid a frozen UI. After reading some horror stories about using CoreData on a background thread I was actually surprised how straight forward it was. I used Apple’s sample code ThreadedCoreData as a guidance. When I tested my changes everything went smooth. Until I started to touch my app’s UI while the background process was running. Suddenly the app crashed and the following exception was thrown:
*** Terminating app due to uncaught exception 'NSGenericException',
reason: '*** Collection <__NSCFSet: 0x1f0ec200> was mutated while
being enumerated.'
The exception was being thrown on the background thread during a call to
[NSManagedObjectContext executeFetchRequest:error:]
What happened?
In Apple’s example code the CoreData operations happen inside the main method of a custom subclass of NSOperation. That subclass is initialized using a custom initWithData: method and then added to a NSOperationQueue.
When doing CoreData operations on a background thread it is important that the background thread uses it’s own instance of NSManagedObjectContext. And this NSManagedObjectContext has to be instantiated on the background thread.
And that was the problem.
In Apple’s example the NSManagedObjectContext for the background thread is instantiated in the custom initWithData: method of the custom ParseOperation class. But that method is called on the main thread when instantiating a new ParseOperation object before adding it to the NSOperationQueue. So the NSManagedObjectContext instance is created on the main thread!
The solution was quite simple:
After moving the code that instantiates the NSManagedObjectContext from the initWithData: method (called from the main thread) to the main method (called from the background thread), the problem vanished and the code run smoothly.
So, when using CoreData in a background thread make sure that the background thread has it’s own NSManagedObjectContext instance that was created on the background thread!
1
Andreassaid atHi,
thank you VERY much for posting this! Saved me hours 🙂
2
stonebitssaid atThanks — it worked, saved a TON of time!
3
Algosaid atYou just saved another soul!
4
lydiasaid atThanks lot for giving this solution.Just make sure with it, so in background thread function we just need to initiate Nsmanagedobjectcontext right? If i have another viewcontroller that need this like process means it have any problem with multiple managedobjectcontext privately
5
Lydiasaid atBackground theread{
var appdel_back = AppDelegate()
var context_back = NSManagedObjectContext()
appdel_back = (UIApplication.sharedApplication().delegate as! AppDelegate)
context_back = appdel_back.managedObjectContext
}
Is this is enough , or need to do a create subclasses
6
Jörnsaid atHi Lydia,
thanks for your comments. You only need to create the background thread NSManagedObjectContext when you really need it. So whenever you need to do CoreData operations on a background thread, instantiate a NSManagedObjectContext instance on the background thread and use it for your core data operations. In your second comment you create a background AppDelegate? You should avoid that, as an App should only have one AppDelegate.
Best,
Jörn
7
Andreasaid atWould you please share here an example of how to create the NSManagedObjectContext on the background thread?
I am using CoreDataStack.shared.persistentContainer.newBackgroundContext()
Thanks!