viewContext.setQueryGenerationFrom 试图解决 CoreData 项目中涉及事务历史的什么问题?

问题描述 投票:0回答:3

我遇到过2个CoreData演示项目,其中涉及交易历史。

两者都在使用

viewContext.setQueryGenerationFrom(.current)

当他们初始化 CoreData 堆栈时。


来自 raywenderlich 的 FireballWatch 演示

演示摘自 https://www.raywenderlich.com/14958063-modern-efficient-core-data

作者试图演示,如何利用交易历史记录,在批量插入后正确更新UI。

但是,尚不清楚

viewContext.setQueryGenerationFrom(.current)
试图解决什么问题。

代码:https://github.com/yccheok/FireballWatch_Materials/blob/main/final/FireballWatch/Model/Persistence.swift#L100

文章的简要解释https://www.raywenderlich.com/14958063-modern-efficient-core-data并没有过多讲述

setQueryGenerationFrom
背后的想法。

您将视图上下文固定到最近的事务 通过调用 setQueryGenerationFrom(_:) 来实现持久存储。然而, 因为设置查询生成仅与 SQLite 兼容 store,只有当 inMemory 为 false 时才这样做。


将本地商店从 Apple 同步到云端

演示摘自 https://developer.apple.com/documentation/coredata/synchronizing_a_local_store_to_the_cloud

它试图演示如何使用交易历史记录,以防止与CloudKit同步后的数据重复。

但是,目前还不清楚

viewContext.setQueryGenerationFrom(.current)
想要解决什么问题。

代码:https://github.com/yccheok/SynchronizingALocalStoreToTheCloud/blob/main/CoreDataCloudKitDemo/DataProvider/CoreDataStack.swift#L89

setQueryGenerationFrom
的想法背后没有给出太多解释。


实验

无论我在 CoreData 堆栈中包含

viewContext.setQueryGenerationFrom(.current)
还是排除
viewContext.setQueryGenerationFrom(.current)
,我在这两种情况下都会得到相同的观察结果。

  • 在保存新的
    NSManagedObject
    并调用
    context.save
    后,能够立即观察 UI 更新。
  • 在编辑现有的
    NSManagedObject
    并调用
    context.save
    后,能够立即观察 UI 更新。
  • 在执行批量
    NSBatchUpdateRequest
    操作并调用
    mergeChanges
    后,能够立即观察 UI 更新。
  • 在执行批量
    NSBatchDeleteRequest
    操作并调用
    mergeChanges
    后,能够立即观察 UI 更新。

对于

setQueryGenerationFrom

所做的事情有一些很好的图形解释

https://cocoacasts.com/what-are-core-data-query- Generations

但是,我无法将其与

setQueryGenerationFrom
试图解决什么样的实际问题联系起来。

有谁知道,viewContext.setQueryGenerationFrom 在涉及事务历史的 CoreData 项目中试图解决什么问题? 如果有一个可靠的演示代码示例,以展示 setQueryGenerationFrom 解决了什么样的问题,我将不胜感激。谢谢。

ios swift core-data
3个回答
6
投票

它只是固定一些上下文快照,因此您接下来的所有查询都与该快照一起工作,而与固定时刻之后发生的情况无关。这就像从 GitHub 分离结账 - 每个人都可以继续,但你的工作没有沙箱。

这是一致性的保证,这可能是某些请求序列所需要的,在这些请求序列之间不应发生任何更改。

要固定,我们使用

viewContext.setQueryGenerationFrom(.current)

要取消固定并继续使用 kind-of-HEAD,我们使用

viewContext.setQueryGenerationFrom(nil)

更多说明参见Apple 的文章


2
投票

以演示项目CoreDataCloudKitDemo为例,它是支持device1正在编辑帖子,device2删除帖子的情况。你可以找一下这段代码:

// The selected post was changed, and the user isn’t editing it.
// Show an alert, and go back to the main view to reload everything after the user taps reload.
let alert = UIAlertController(title: "Core Data CloudKit Alert",
                              message: "This post has been deleted by a peer!",
                              preferredStyle: .alert)

借助查询生成的魔力,在 device2 删除帖子后,我们将能够立即在正在编辑帖子的 device1 上弹出此警报。

在主列表视图上,没有问题,因为该项目可能只是以动画方式从列表中移出。问题在于详细信息视图、将显示什么以及如果用户编辑详细信息视图的值会发生什么。

在示例代码中,通常如下

container.viewContext.automaticallyMergesChangesFromParent = true

就像在详细信息视图中一样,通常我们会持有一个对象,一旦它被删除,该对象本身就会变得无效,访问它或属性会导致意外的行为。


0
投票

它解决了如果你需要多次读取,数据不会在你脚下改变的问题,这里是一个例子,其中 context2 被固定,所以它在 context1 插入之前和之后的读取,返回相同的结果:

func testPinning(context1: NSManagedObjectContext) throws {
    
    let context2 = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
    context2.persistentStoreCoordinator = context1.persistentStoreCoordinator
    try context2.setQueryGenerationFrom(.current)
    print("context2: pinned to current")
    
    let authorName = "Charles Dickens"
    
    let fr = Author.fetchRequest()
    fr.predicate = NSPredicate(format: "name = %@", authorName)
    
    let fetch = {
        let results = try context1.fetch(fr)
        let results2 = try context2.fetch(fr)
        print("context1: fetched \(results.count), context2: fetched \(results2.count)")
    }
    try fetch()
    
    let author = Author(context: context1)
    author.name = authorName
    try context1.save()
    print("context1: inserted & saved")
    
    try fetch()
    
    try context2.setQueryGenerationFrom(.current)
    print("context2: repinned to current")
    
    try fetch()
    
}

输出是

context2: pinned to current
context1: fetched 0, context2: fetched 0
context1: inserted & saved
context1: fetched 1, context2: fetched 0 // because context2 is pinned it did not find the newly inserted author.
context2: repinned to current
context1: fetched 1, context2: fetched 1

正如你所看到的,因为 context2 被固定,在 context1 插入后,当 context2 再次获取时,它仍然没有结果。

您可能需要多次获取的原因是为了某些复杂的 UI,例如首先获取一个表,然后获取每一行,如果在第一次获取后后台上下文删除了一行,那么第二次获取将无法找到它。但是通过固定它可以找到它。

© www.soinside.com 2019 - 2024. All rights reserved.