Неожиданное нарушение многопоточности данных

Я использую Apple concurrency отладчик основных данных.

-com.apple.CoreData.ConcurrencyDebug 1

Время от времени я получил __Multithreading_Violation_AllThatIsLeftToUsIsHonor__, даже я почти уверен, что нить не нарушена.

Это часть кода, в котором происходит исключение (код является частью протокола, который расширяет 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 []
    }
}

Код выполняется в контексте блока perform:.

Вот информация о потоках:

введите описание изображения здесь

и информацию об отладчике, чтобы подтвердить, что выполнение выполняется в правом NSManagedContext:

(lldb) po Context.current
<StoreContext: 0x7f854b556610>

Имя объекта успешно извлечено:

po fetchRequest.entityName!
"Position"

Предикат построен из чистых объектов String (никаких управляемых объектов вообще не используется):

(lldb) po fetchRequest.predicate!
ANY employees.company.id == "282372"

В этом случае дескрипторы сортировки вообще не используются:

po fetchRequest.sortDescriptors!
0 elements

Предел полностью игнорируется.

Что мне не хватает? Кто-нибудь знает, что здесь может быть не так?

Edit:

Чтобы уточнить, Context.current устанавливается непосредственно перед отправкой блока:

Context.current = managedObjectContext
managedObjectContext.performAndWait {
   //...
}

Вы можете увидеть на скриншоте, что Thread 13 работает на Queue: NSManagedObject 0x7f854b556610 (serial). Кроме того, когда возникает исключение Context.current возвращает <StoreContext: 0x7f854b556610>. Если посмотреть на адрес памяти, то он легко выполнит в правой очереди.

Ответ 1

Сохранение "текущего" фонового контекста в глобальном состоянии является плохой практикой. Я не могу указать, где именно в вашем коде запутался, но неожиданное может случиться с глобальным состоянием, когда задействовано многопоточность. Измените функцию find, чтобы принять параметр context в качестве параметра. Это позволит избежать использования какого-либо глобального состояния и, скорее всего, устранит вашу проблему.

Ответ 2

Рекомендуется избегать использования API .performAndWait, который будет использоваться только в самых редких случаях, когда все остальное не удалось!
Оцените морфинг Context.current до managedObjectContext.perform во всем приложении.
Результатом этого изменения будет добавление асинхронности во всех операциях с вашей базой данных.
Это может показаться массовым изменением, чтобы спросить, но поверьте мне, просто решите рассматривать Core Data как полностью асинхронный API, и жизнь будет намного лучше.
Я уверен, что текущая авария, с которой вы сталкиваетесь, является результатом сложного результата поврежденного поведения .performAndWait

Это, this и это, некоторые хорошие чтения по теме.