As mentioned in WWDC 2014 session 225 (Whatʼs New in Core Data), Core Data on iOS 8 and OS X Yosemite now support the command line argument -com.apple.CoreData.ConcurrencyDebug 1
to enable assertions that detect violations of Core Dataʼs concurrency contract.
In my experiments with this, I have found that it works under iOS 8 beta 1 (both on the device and in the simulator), but I seem to have found a false positive, i.e. the framework is throwing a multithreading violation exception where it should not do so. At least that's what I believe.
Question: is the code below correct or am I doing something that violates Core Dataʼs threading model?
What I do is set up a very simple Core Data stack (with an in-memory store, for simplicity's sake) with a managed object context named backgroundContext
with private queue concurrency. I then invoke performBlockAndWait { }
on that context and in the block I create a new managed object, insert it into the context, and save.
The save operation is where I get the multithreading violation exception from Core Data.
var backgroundContext: NSManagedObjectContext?
func setupCoreDataStackAndViolateThreadingContract()
{
let objectModelURL = NSBundle.mainBundle().URLForResource("CoreDataDebugging", withExtension: "momd")
let objectModel: NSManagedObjectModel? = NSManagedObjectModel(contentsOfURL: objectModelURL)
assert(objectModel)
// Set up a simple in-memory Store (without error handling)
let storeCoordinator: NSPersistentStoreCoordinator? = NSPersistentStoreCoordinator(managedObjectModel: objectModel)
assert(storeCoordinator)
let store: NSPersistentStore? = storeCoordinator!.addPersistentStoreWithType(NSInMemoryStoreType, configuration: nil, URL: nil, options: nil, error: nil)
assert(store)
// Set up a managed object context with private queue concurrency
backgroundContext = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
assert(backgroundContext)
backgroundContext!.persistentStoreCoordinator = storeCoordinator!
// Work on the background context by using performBlock:
// This should work but throws a multithreading violation exception on
// self.backgroundContext!.save(&potentialSaveError)
backgroundContext!.performBlockAndWait {
NSEntityDescription.insertNewObjectForEntityForName("Person", inManagedObjectContext: self.backgroundContext!) as NSManagedObject
person.setValue("John Appleseed", forKey: "name")
var potentialSaveError: NSError?
// In the following line: EXC_BAD_INSTRUCTION in
// `+[NSManagedObjectContext __Multithreading_Violation_AllThatIsLeftToUsIsHonor__]:
let didSave = self.backgroundContext!.save(&potentialSaveError)
if (didSave) {
println("Saving successful")
} else {
let saveError = potentialSaveError!
println("Saving failed with error: \(saveError)")
}
}
}
I have tested essentially the same code in Objective-C and got the same result so I doubt it is a Swift problem.
Edit: If you want to run the code yourself, I have put a project on GitHub (requires Xcode 6/iOS 8 beta).
Apple confirmed this as a bug. It has been fixed with Xcode 6 beta 4.
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