目前,以下是我关于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回调中运行代码。如果我们使用Thread来检查。isMainThread(返回false)在loadFileRepresentation回调中,它是在后台线程中执行的。
当我执行调用NSAttachmentRepository。INSTANCE。isExist(name)在函数中线程。isMainThread是假的,我将得到以下崩溃
(NSManagedObjectContext CoreData ' +
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上下文。从后台线程计数?
###仅仅使用背景背景是不够的。您需要在它自己的队列上使用该上下文。你检查你不是在主队列上运行,但你可以在任何队列上,而后台上下文只在其中一个上工作。当你在错误的队列上使用它时,你看到的错误消息是Core Data说的。
任何时候你使用backgroundContext你需要包装代码在一个调用执行或performAndWait,以确保你的代码运行在后台上下文的队列。因为你的isExist函数是同步的,它需要使用performAndWait,这样它可以在返回之前得到一个结果。
###感谢汤姆·哈灵顿的提示
下面是关于如何处理线程问题的代码片段。
func isExist(_ name: String) -> Bool {
let coreDataStack = CoreDataStack.INSTANCE
let viewContext = coreDataStack.persistentContainer.viewContext
var result = false
viewContext.performAndWait {
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 {
result = true
} else {
result = false
}
} catch {
error_log(error)
}
}
return result
}