为啥我会为这个最简单的 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 获得随机/错误的输出?