【发布时间】:2021-10-19 01:24:29
【问题描述】:
目前,以下是我关于 CoreData 的实现。
class CoreDataStack {
static let INSTANCE = CoreDataStack()
private init() {
}
private(set) lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "xxx")
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
return container
}()
private(set) lazy var backgroundContext: NSManagedObjectContext = {
let backgroundContext = persistentContainer.newBackgroundContext()
backgroundContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
return backgroundContext
}()
}
class NSAttachmentRepository {
static let INSTANCE = NSAttachmentRepository()
private init() {
}
func isExist(_ name: String) -> Bool {
let coreDataStack = CoreDataStack.INSTANCE
let viewContext = coreDataStack.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSAttachment>(entityName: "NSAttachment")
fetchRequest.fetchLimit = 1
fetchRequest.predicate = NSPredicate(format: "name == %@", name)
do {
let count = try viewContext.count(for: fetchRequest)
if count > 0 {
return true
}
} catch {
error_log(error)
}
return false
}
}
我处理核心数据的策略是
- 为了从主线程(UI线程)执行非阻塞写调用,我将使用
CoreDataStack.INSTANCE.backgroundContext - 为了从主线程(UI线程)执行阻塞读取调用,我将使用
CoreDataStack.INSTANCE.persistentContainer.viewContext
这个工作一直很好,直到我需要执行以下操作
- 从后台线程(非 UI 线程)执行阻塞读取调用
我们需要运行PHPickerViewControllerDelegate的loadFileRepresentation回调中的代码。如果我们在loadFileRepresentation 回调中使用Thread.isMainThread(返回false)进行检查,它会在后台线程中执行。
当我在 Thread.isMainThread 为 false 的函数中调用 NSAttachmentRepository.INSTANCE.isExist(name) 时,我会遇到以下崩溃
CoreData`+[NSManagedObjectContext Multithreading_Violation_AllThatIsLeftToUsIsHonor]:
我尝试通过将代码从使用 coreDataStack.persistentContainer.viewContext 修改为 coreDataStack.backgroundContext 来“解决”问题
func isExist(_ name: String) -> Bool {
let coreDataStack = CoreDataStack.INSTANCE
////let viewContext = coreDataStack.persistentContainer.viewContext
let backgroundContext = coreDataStack.backgroundContext
let fetchRequest = NSFetchRequest<NSAttachment>(entityName: "NSAttachment")
fetchRequest.fetchLimit = 1
fetchRequest.predicate = NSPredicate(format: "name == %@", name)
do {
////let count = try viewContext.count(for: fetchRequest)
let count = try backgroundContext.count(for: fetchRequest)
if count > 0 {
return true
}
} catch {
error_log(error)
}
return false
}
但是,我仍然遇到同样的崩溃错误。
您知道如何从后台线程执行 CoreData context.count 吗?
【问题讨论】:
-
你真的确认从主线程写入是一个性能问题吗?你要写多少数据?
-
您好,我们对写操作完全没问题。我们的目标是执行读取操作。读取操作是从外部后台线程执行的。我更新了我的问题,以解释外部后台线程的来源。
-
好的,抱歉,我弄错了,但我的问题仍然存在 - 性能有问题吗?如果需要,为什么不直接将代码分派到主队列?除非您正在阅读数百万条记录,否则 Core Data 非常快。
-
我没有收到错误,对我来说工作正常。您是否尝试通过将 fetch 请求包装到
backgroundContext.performAndWait(_:)中来确保队列? -
您应该使用
aContext.perform{}或aContext.performAndWait{}确保里面的代码在上下文的正确线程中完成。