为什么我在这个最简单的 NSAsynchronousFetchRequest 用例中获得 Multithreading_Violation_AllThatIsLeftToUsIsHonor?

Che*_*eng 5 core-data ios swift

我想开始学习使用NSAsynchronousFetchRequest参考https://www.marcosantadev.com/coredata_crud_concurrency_swift_2/

我有最简单的用例NSAsynchronousFetchRequest

NSA同步获取请求

// Call from UI main thread
func X() {
    let fetchRequest = NSFetchRequest<NSPlainNote>(entityName: "NSPlainNote")
    
    let asynchronousFetchRequest = NSAsynchronousFetchRequest(fetchRequest: fetchRequest) { asynchronousFetchResult in
        guard let result = asynchronousFetchResult.finalResult as? [NSPlainNote] else { return }
    }

    let coreDataStack = CoreDataStack.INSTANCE
    // backgroundContext created via persistentContainer.newBackgroundContext()
    let backgroundContext = coreDataStack.backgroundContext
    
    backgroundContext.perform {
        do {
            try backgroundContext.execute(asynchronousFetchRequest)
        } catch let error {
            backgroundContext.rollback()
            
            error_log(error)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,运行上面的代码会出现以下错误

CoreData`+[NSManagedObjectContext Multithreading_Violation_AllThatIsLeftToUsIsHonor ]:

如果我修改代码NSFetchRequest直接使用.

NSFetchRequest

// Call from UI main thread
func X() {
    let fetchRequest = NSFetchRequest<NSPlainNote>(entityName: "NSPlainNote")

    let coreDataStack = CoreDataStack.INSTANCE
    // backgroundContext created via persistentContainer.newBackgroundContext()
    let backgroundContext = coreDataStack.backgroundContext
    
    backgroundContext.perform {
        do {
            let nsPlainNotes = try fetchRequest.execute()
        } catch let error {
            backgroundContext.rollback()
            
            error_log(error)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

事情运作良好。我想知道我的NSAsynchronousFetchRequest代码版本有什么问题吗?

这是我的CoreDataStack.swift,仅供参考。

CoreDataStack.swift

import CoreData

class CoreDataStack {
    static let INSTANCE = CoreDataStack()
    
    private init() {
    }
    
    private(set) lazy var persistentContainer: NSPersistentContainer = {
        let container = NSPersistentContainer(name: "wenote")
        
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                // This is a serious fatal error. We will just simply terminate the app, rather than using error_log.
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        
        // So that when backgroundContext write to persistent store, container.viewContext will retrieve update from
        // persistent store.
        container.viewContext.automaticallyMergesChangesFromParent = true
        
        // TODO: Not sure these are required...
        //
        //container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
        //container.viewContext.undoManager = nil
        //container.viewContext.shouldDeleteInaccessibleFaults = true
        
        return container
    }()
    
    private(set) lazy var backgroundContext: NSManagedObjectContext = {
        let backgroundContext = persistentContainer.newBackgroundContext()

        // Similar behavior as Android's Room OnConflictStrategy.REPLACE
        backgroundContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
        
        // TODO: Not sure these are required...
        //backgroundContext.undoManager = nil
        
        return backgroundContext
    }()
}
Run Code Online (Sandbox Code Playgroud)

附加信息

请注意,在NSAsynchronousFetchRequest示例中,即使backgroundContext.perform没有使用。

// Call from UI main thread
func X() {
    let fetchRequest = NSFetchRequest<NSPlainNote>(entityName: "NSPlainNote")

    let asynchronousFetchRequest = NSAsynchronousFetchRequest(fetchRequest: fetchRequest) { asynchronousFetchResult in
        guard let result = asynchronousFetchResult.finalResult as? [NSPlainNote] else { return }
    }

    let coreDataStack = CoreDataStack.INSTANCE
    // backgroundContext created via persistentContainer.newBackgroundContext()
    let backgroundContext = coreDataStack.backgroundContext

    do {
        try backgroundContext.execute(asynchronousFetchRequest)
    } catch let error {
        backgroundContext.rollback()

        error_log(error)
    }
}
Run Code Online (Sandbox Code Playgroud)

同样的致命错误仍然发生。

请注意,只有通过使用启动时传递的参数编辑模式才会触发此致命错误

-com.apple.CoreData.ConcurrencyDebug 1
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

我什至尝试从https://github.com/abhishekbedi1432/Core-Data-Asynchronous-Fetching/tree/master执行一些使用NSAsynchronousFetchRequest.

如果我不启用-com.apple.CoreData.ConcurrencyDebug 1,来自 github 的示例项目能够毫无问题地执行异步获取。然而,一旦-com.apple.CoreData.ConcurrencyDebug 1启用,它也会遇到同样的致命错误。

小智 0

您必须为当前 Core Data 容器创建一个新上下文,无论是子上下文还是根父上下文,如下所示:

let backgroundContext = persistentContainer.newBackgroundContext()
backgroundContext.parent = persistentContainer.viewContext
Run Code Online (Sandbox Code Playgroud)

使用多个上下文的详尽解释在这里