当保存在后台异步完成时,我应该如何保证从嵌套上下文中的不同线程获取结果是最新的



我已经阅读了performBlock:和performBlockAndWait:之间的以下行为差异:?但没能找到我问题的答案。

以下代码是从RayWenderlich视频中提取的。具体来说,在10:05,代码是,类似于这样的东西

class CoreDataStack {
var coordinator : NSPersistentStoreCoordinator
init(coordinator: NSPersistentStoreCoordinator){
self.coordinator = coordinator
}
// private, parent, in background used for saving
private lazy var savingContext : NSManagedObjectContext = {
let moc = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
moc.persistentStoreCoordinator = coordinator
return moc
}()
lazy var mainManagedObjectedContext : NSManagedObjectContext = {
let moc = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
moc.parent = self.savingContext
return moc
}()
func saveMainContext() {
guard savingContext.hasChanges || mainManagedObjectedContext.hasChanges else {
return
}
mainManagedObjectedContext.performAndWait {
do {
try mainManagedObjectedContext.save()
}catch let error{
fatalError(error.localizedDescription)
}
}
savingContext.perform {
do {
try self.savingContext.save()
}catch let error{
fatalError(error.localizedDescription)
}
}
}
}

据我所知,主上下文只是将更改传递给它的父上下文,这是一个私有的背景上下文。它同步地执行此操作。

然后,父上下文(private context)在后台线程中异步对sqlite进行实际保存。长话短说,这对我们的表演有很大帮助。但是数据完整性呢?!

想象一下,如果我要这样做:

let coredataManager = CoreDataStack()
coredataManager.saveMainContext() // save is done asynchronously in background queue
coredataManager.mainManagedObjectedContext.fetch(fetchrequest) 

我如何保证我的获取正在读取最新和更新的结果?

如果我们异步写入,那么同时进行另一次读取是否有可能导致意外结果,即保存更改的结果可能存在或不存在?

编辑:我对下面的代码进行了改进。我可以在completionHandler参数中进行保存。但这并不能解决整个问题。如果我从其他地方的mainQueue发出fetchRequest,但它不知道同时发生了保存,该怎么办?

enum SaveStatus{
case noChanges
case failure
case success
}

func saveMainContext(completionHandler: (SaveStatus -> ())) {
guard savingContext.hasChanges || mainManagedObjectedContext.hasChanges else {
completionHandler(.noChanges)
return
}
mainManagedObjectedContext.performAndWait {
do {
try mainManagedObjectedContext.save()
}catch let error{
completionHandler(.failure)
fatalError(error.localizedDescription)
}
}
savingContext.perform {
do {
try self.savingContext.save()
completionHandler(.succes)
}catch let error{
completionHandler(.failure)
fatalError(error.localizedDescription)
}
}
}

mainManagedObjectContext的所有调用都将是同步的,因此会被阻塞。如果您调用saveMainContext(),然后立即调用mainManagedObjectedContext.fetch(fetchrequest),则在保存请求完成之前,即使保存/提取请求来自不同的队列,提取请求也不会通过(请参阅上面链接中关于FIFO的段落)。

当您执行提取请求时,您不是从持久存储中提取的-您是从刚刚更新的子容器中提取的。您不需要等待更改提交到持久存储,因为您不需要从那里访问数据。子容器将为您提供最新的更改。

子容器容器-它将在内存中保存您的最新更改(而不是存储在磁盘上-这是持久容器的工作)。

这里真正的问题是,CoreDataStack应该实现singleton模式,以防止实例化相同容器的多个版本(从技术上讲,这些容器仍然在同一个线程上,因此是序列化的,但访问容器不是线程安全的)。换句话说,每次实例化CoreDataStack()时,都会创建一个新的savingContextmainManagedObjectedContext

相反,只实例化一次。

class CoreDataStack {
var coordinator: NSPersistentStoreCoordinator
public static let sharedInstance = CoreDataStack()
private override init() {
self.coordinator = NSPersistantStoreCoordinator()
}
...
rest of your code here
...
}

然后这样调用:

CoreDataStack.sharedInstance.saveMainContext()

(请参阅此链接:"子对象与父对象具有相同的对象吗?")

唯一一个孩子不会与父母同步的情况是,你有多个孩子访问同一个父母,但这里似乎不是这样。

这个问题并不是核心数据特有的。

这是一个经典的读写问题。

保护数据源的常见方法是使用串行队列访问数据源。否则,如果没有串行队列,您将出现读写问题。

在以下示例中:

let coredataManager = CoreDataStack() // 1
coredataManager.saveMainContext() // 2 save is done asynchronously in background queue
coredataManager.mainManagedObjectedContext.fetch(fetchrequest) // 3

CCD_ 9将从串行队列访问。因此,即使第2行的写入是异步完成的,第3行的读取也必须等待串行队列被解除阻塞。

最新更新