为啥我会为这个最简单的 NSAsynchronousFetchRequest 用例获得 Multithreading_Violation_AllThatIsLeftToUsIsHonor?

Posted

技术标签:

【中文标题】为啥我会为这个最简单的 NSAsynchronousFetchRequest 用例获得 Multithreading_Violation_AllThatIsLeftToUsIsHonor?【英文标题】:Why I am getting Multithreading_Violation_AllThatIsLeftToUsIsHonor for this simplest NSAsynchronousFetchRequest use case?为什么我会为这个最简单的 NSAsynchronousFetchRequest 用例获得 Multithreading_Violation_AllThatIsLeftToUsIsHonor? 【发布时间】:2021-12-31 04:24:25 【问题描述】:

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

我有NSAsynchronousFetchRequest这个最简单的用例

NSAsynchronousFetchRequest

// 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)
        
    


但是,运行上面的代码会给我以下错误

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)
        
    

一切正常。请问,我的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
    ()


其他信息

请注意,在 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)
    

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

请注意,只有通过使用启动时传递的参数

编辑架构,才会触发此致命错误
-com.apple.CoreData.ConcurrencyDebug 1

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

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

【问题讨论】:

尝试用直接调用 newBackgroundContext() 替换你的惰性 backgroundContext 变量,这样你每次都会得到一个新的上下文 是的。试试看。发生同样的致命错误。我在“其他信息”下添加了更多有用的发现... @CheokYanCheng 请看我对你其他问题的回答——它也涉及到这个主题***.com/a/70637770/921573 【参考方案1】:

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

let backgroundContext = persistentContainer.newBackgroundContext()
backgroundContext.parent = persistentContainer.viewContext

使用多个上下文的详尽解释是here。

【讨论】:

我认为这不对。 newBackgroundContext() 方法给出了一个已经有父上下文的上下文。如果您尝试设置新的,则会引发内部一致性错误。 哦,你是对的。我唯一不同的部分是核心数据堆栈(我使用了 Xcode 提供的那个),但是将其更改为 OP 的实现会在分配新的父代码时引发异常。在对附加的 gh repo 的代码进行了一些挖掘之后,看起来 NSAsynchronousFetchRequest 只是在设置为使用私有队列的上下文上不起作用,就像设置为主队列的上下文一样,它按预期工作(viewContext 和一个手动初始化并分配给容器)。【参考方案2】:

对我来说,当我向异步获取请求提供 estimatedResultCount 值时,它会按预期工作。

【讨论】:

以上是关于为啥我会为这个最简单的 NSAsynchronousFetchRequest 用例获得 Multithreading_Violation_AllThatIsLeftToUsIsHonor?的主要内容,如果未能解决你的问题,请参考以下文章

错误:WatchActivity 无法解析为一种类型,为啥我会得到这个? [复制]

为啥我会从 VBA Select Case 获得随机/错误的输出?

为啥我会从“double”转换为“float”,可能会丢失数据警告?(c)

为啥我会得到这个错误的输出?

为啥我会收到这个 TypeError?

为啥我会收到这些错误,java truetype 错误?