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

2023-12-27

我已阅读以下内容PerformBlock: 和 PerformBlockAndWait: 之间的行为差​​异? https://stackoverflow.com/questions/32198678/behavior-differences-between-performblock-and-performblockandwait但无法找到我的问题的答案。

以下代码摘自 RayWenderlichvideo https://www.youtube.com/watch?v=lMT96wUsjMQ。具体在10:05 https://youtu.be/lMT96wUsjMQ?t=605代码是某物像这样:

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)
            }
        }
    }
}

据我了解,发生的情况是主上下文只是将更改传递给其父上下文,这是一个私有的后台上下文。它同步执行此操作。

然后父级私有上下文在后台线程中对 sqlite 进行实际保存异步地。长话短说,这对我们的性能有很大帮助。但数据完整性又如何呢?

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

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

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

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

EDIT:我用下面的代码进行了改进。我可以让我的保存接受一个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 的段落)。

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

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

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

相反,只需实例化一次。

class CoreDataStack {

    var coordinator: NSPersistentStoreCoordinator

    public static let sharedInstance = CoreDataStack()

    private override init() {
        self.coordinator = NSPersistantStoreCoordinator()
    }

    ...
    rest of your code here
    ...
}

并像这样调用:

CoreDataStack.sharedInstance.saveMainContext()

(See 这个链接 https://stackoverflow.com/questions/30089401/core-data-should-i-be-fetching-objects-from-the-parent-context-or-does-the-chil回复:“孩子和父母有相同的对象吗?”)

子级不会与父级同步的唯一情况是您有多个子级访问同一个父级 - 但这里的情况似乎并非如此。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

随机推荐