I have problem with debugging Core Data concurrency issues.
In my scheme I enabled:
-com.apple.CoreData.Logging.stderr 1
-com.apple.CoreData.ConcurrencyDebug 1
I would like to fetch records from Core Data in background thread.
So in my UIViewController I create DispatchQueue.global(qos: .background).async where I call Core Data to fetch records.
I am using parent/child:
func performStationsFetch(filter: String = "") -> [Stations] {
let privateMOC = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
let parentMOC = (UIApplication.shared.delegate as! AppDelegate).managedObjectContext
privateMOC.parent = parentMOC
var fetchResults: [Stations] = []
privateMOC.performAndWait {
let fetchRequest: NSFetchRequest<Stations> = Stations.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "name contains[c] %@", filter)
do {
fetchResults = try privateMOC.fetch(fetchRequest)
} catch {
print("Fetch error")
}
}
return fetchResults
}
The problem is if I call privateMOC.perform the code works fine.
But if I call privateMOC.performAndWait the code crashes and gives me the +[NSManagedObjectContext __Multithreading_Violation_AllThatIsLeftToUsIsHonor__]
Does anyone have a suggestion what can be a problem?
The multi-threading violation occurs when you try to access the results of the function. If you use perform, the results are empty - so there is no problem. If you use performAndWait, then you are returning managedObjects which are almost certainly being accessed on the wrong thread.
What is worse if that you can also get a bad-access-error if you try to use those objects. ManagedObjects are not like normal NSObjects. A managedObject is just an objectId and a pointer to context. Whenever it needs to access its properties it forwards the request to the context. So if that context gets removed, then those objects cannot work. In your code the context that is backing the results is removed from memory when the function ends. (NOTE: The context is not always immediately removed. In a debug environment the context may be kept around for a little while. In production it is released immediately at the end of the function).
So accessing the results of the function is illegal for two reasons - 1) you are on the wrong thread and 2) it does not having a backing context.
There are a few solutions. One would be to copy the values that you need from the managedObject and return an array or strings, or a custom object (a NSObject subclass).
Another solution is to do the fetch on the main context which is around for the lifetime of your application.
Alternately you can pass the context to be used to the function. And it is the responsibility of the caller to ensure that the context is kept around as long as the managedObjects are needed.
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