我有一个 NSManagedObjectContext
的初始化,它是由 newBackgroundContext 的 persistentContainer
如下。
managedContext = coreDataStack.persistentContainer.newBackgroundContext()
是这样的 persistentContainer
看起来像。
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "myXCDataModelName")
container.loadPersistentStores(completionHandler: { [weak self] (_, error) in
if let self = self,
let error = error as NSError? {
print("error!")
}
})
return container
}()
我在用 newBackgroundContext
的任何CRUD操作,以确保 CoreData
可以安全地完成,无论哪个线程试图在其上进行修改。managedContext
而不是确保或强制每个操作都在主线上完成。
我有一个 saveContext
方法,我试图对 managedContext
里面 performAndWait
块如下。
managedContext.performAndWait {
do {
guard UIApplication.shared.isProtectedDataAvailable,
managedContext.hasChanges else {
return
}
try managedContext.save()
} catch {
print("error!")
}
}
它看起来像 performAndWait
大部分时间是在主线程上运行,但当它在另一个线程上运行时,线程检查器会因以下检查而产生警告。UIApplication.shared.isProtectedDataAvailable
因为它应该在主线程上完成。
我决定在主线程上运行一个选择器来进行这个检查,所以我声明了一个叫做 isProtectedDataAvailable
Bool,在类级将其默认为false,然后在这个选择器运行时更新其值。
@objc private func checker() {
isProtectedDataAvailable = UIApplication.shared.isProtectedDataAvailable
}
现在我重构了 performAndWait
块,以便在主线程上运行检查,如果它被其他线程调用。
managedContext.performAndWait {
do {
if Thread.isMainThread {
checker()
} else {
print("RUNNING ON ANOTHER THREAD")
Thread.current.perform(#selector(checker),
on: Thread.main,
with: nil,
waitUntilDone: true,
modes: nil)
}
guard isProtectedDataAvailable,
managedContext.hasChanges else {
return
}
try managedContext.save()
} catch {
print("error!")
}
}
当我在模拟器或真实设备上运行它时,它似乎工作得很好,我产生了不同的核心数据相关操作,这些操作将触发主线程和后台线程上的保存上下文。
但是发生的情况是,如果我把一些断点放进 performAndWait
块,并停止执行,以检查代码块的工作情况,它有时会导致应用程序冻结,当我继续执行,就像一个死锁发生。我不知道这是否与使用断点停止执行有某种关系,还是我的实现出了问题,尽管没有断点也能正常工作,没有出现应用冻结之类的情况。
我很担心,因为在使用这个解决方案之前,我尝试过在主线程上同步,灵感来自于以下内容 回答 只需切换到主线程进行检查,如下图所示(导致应用程序冻结,我认为是死锁),在里面的 performAndWait
块。
var isProtectedDataAvailable = false
if Thread.isMainThread {
isProtectedDataAvailable = UIApplication.shared.isProtectedDataAvailable
} else {
DispatchQueue.main.sync {
isProtectedDataAvailable = UIApplication.shared.isProtectedDataAvailable
}
}
我不得不用 sync
而不是 async
的更新值,因为我必须检索更新后的 isProtectedDataAvailable
在继续执行之前。
有什么想法吗?
我想你的问题是关于正确的方法,这样代码就能正常工作,并且允许设置任何断点而不陷入死锁。)
为什么不试试这种方式(从你所说的,我看不出有什么理由反对它)。
UIApplication.shared.isProtectedDataAvailable
在主线上。(我猜想,还有更复杂的条件,不仅要满足 UIApplication.shared.isProtectedDataAvailable = true
但为了保持简单...)UIApplication.shared.isProtectedDataAvailable
(和 其他条件)评价为 true
继续在后台线程上进行数据处理和保存。由于 managedContext
是作为一个 newBackgroundContext
它可以在那里使用。主线上的说明
if UIApplication.shared.isProtectedDataAvailable && otherConditions {
DispatchQueue.global(qos: .userInitiated).async {
dataProcessing()
}
}
关于背景线的说明
static func dataProcessing() {
// get the managedContext
let context = AppDelegate.appDelegate.managedContext
context.performAndWait {
if context.hasChanges {
do {
try context.save()
} catch {
print("error!")
}
}
}
}
出于某种原因,将下面的检查移动到主队列之外,并与主队列同步。performAndWait
解决了所有问题。
无论是有断点还是没有断点,都不会发生死锁或应用程序冻结,无论哪个线程触发了包含有 performAndWait
所以现在的方法体是这样的。
var isProtectedDataAvailable = false
if Thread.isMainThread {
isProtectedDataAvailable = UIApplication.shared.isProtectedDataAvailable
} else {
DispatchQueue.main.sync {
isProtectedDataAvailable = UIApplication.shared.isProtectedDataAvailable
}
}
managedContext.performAndWait {
do {
guard isProtectedDataAvailable,
managedContext.hasChanges else {
return
}
try managedContext.save()
} catch {
print("error")
}
}