问题
I'm using Apple's concurrency core data debugger.
-com.apple.CoreData.ConcurrencyDebug 1
From time to time I got __Multithreading_Violation_AllThatIsLeftToUsIsHonor__
, even I'm almost sure threading is not violated.
This is part of code where exception occurs (code is part of protocol that extends NSManagedObject):
public static func find(arrayBy predicate: NSPredicate, sort: [NSSortDescriptor] = [], limit: Int? = nil) -> [Self] {
let fetchRequest = NSFetchRequest<Self>(entityName: "\(Self.self)")
fetchRequest.predicate = predicate
fetchRequest.sortDescriptors = sort
do {
return try Context.current.fetch(fetchRequest) // Exception!!!
} catch let error {
Logger.fatal("Failed to perform fetch: \(error)")
return []
}
}
Code is executed within context's perform:
block.
Here is thread information:
and debuger info to confirm that perform is executed on the right NSManagedContext:
(lldb) po Context.current
<StoreContext: 0x7f854b556610>
Entity name is extracted successfully:
po fetchRequest.entityName!
"Position"
Predicate is constructed of pure String objects (no managed objects used at all):
(lldb) po fetchRequest.predicate!
ANY employees.company.id == "282372"
Sort descriptors are not used at all in this case:
po fetchRequest.sortDescriptors!
0 elements
Limit is completely ignored.
What am I missing? Does anyone has any idea what can be wrong here?
Edit:
To clarify, Context.current
is set just before dispatching the block:
Context.current = managedObjectContext
managedObjectContext.performAndWait {
//...
}
You can see on the screenshot that Thread 13
is running on Queue: NSManagedObject 0x7f854b556610 (serial)
. Also, when exception occurs Context.current
returns <StoreContext: 0x7f854b556610>
. By looking at the memory address it's easy to conclude block is executing on the right queue.
回答1:
Storing the "current" background context in a global state is a bad practice. I can't point out where exactly in your code it is messing up, but unexpected things can happen with global state when multithreading is involved. Change your find
function to accept a context
as a parameter. This will avoid using any global state and is will likely fix your problem.
回答2:
It is recommended to avoid using the .performAndWait
API, to be used only at the rarest of the rare occasions, when everything else has failed!
Evaluate morphing Context.current
to managedObjectContext.perform
throughout the application.
The effect of this change would be addition of asynchronicity in all your database operations.
It may sound of a massive change to ask for but trust me, just decide to treat Core Data as a completely asynchronous API & life will be much better this way.
I'm sure the current crash you are facing is a result of compound result of the corrupted behavior of .performAndWait
This, this and this are some good reads on the topic.
回答3:
Check out this blog to get better understanding of multithreading in Core Data
https://cocoacasts.com/core-data-and-concurrency/
https://cocoacasts.com/more-core-data-and-concurrency/
来源:https://stackoverflow.com/questions/41999983/unexpected-core-data-multithreading-violation