自动合并不同上下文的数据

时间:2019-09-12 10:14:32

标签: swift core-data

我有一个使用CoreData的应用程序,它具有多个背景上下文(NSManagedObjectContext)。

在编写一些测试时,我观察到一种奇怪的行为,似乎与官方文档存在争议: 来自一个上下文的更改会自动传播到另一上下文,而.automaticallyMergesChangesFromParent在这两个上下文中均设置为false。

两个上下文都是从NSPersistentContainer接收的-一个是从.viewContext接收的,另一个是-.newBackgroundContext()函数的。

save所述,在上下文保存中,更改将提交到上下文的父存储,即NSPersistentContainer。
但是实际上,尽管自动将MergesChangesFromParent == false(这是默认值),更改也出现在另一个上下文中。

let persistentContainer = NSPersistentContainer(name: "TESTING")
let mainContext = persistentContainer.viewContext
let otherContext = persistentContainer.newBackgroundContext()

//test entity is created on anotherContext
let entity: TestEntity = NSEntityDescription.insertNewObject
(forEntityName: String(describing: TestEntity.self),
 into: anotherContext) as! TestEntity
entity.statusCode = "Testing"

//prepare fetch request for test entity
let fetchReq: NSFetchRequest<TestEntity> = TestEntity.fetchRequest()
fetchReq.predicate = NSPredicate(format: "statusCode = %@",
 argumentArray: ["Testing"])

//ensure that entity is not present in mainContext
let entityFromMain = try! mainContext.fetch(fetchReq)
XCTAssertEqual(entityFromMain.count, 0)

//save context that has entity
try! otherContext.save()

//ensure that changes from parent store aren't merged automatically
XCTAssertFalse(mainContext.automaticallyMergesChangesFromParent)

//get inserted entity from mainContext
let entityOnMainAfterSaving = try! mainContext.fetch(fetchReq)

//entity is present in mainContext
XCTAssertTrue(entityOnMainAfterSaving.count > 0)

预期的输出-尽管没有刷新mainContext,entityOnMainAfterSaving不应包含新创建的实体,但它已经存在。

更新: 我问这个问题是因为在我的应用中,存在以下情况:
1.在otherContext中更改实体的属性
2. otherContext已保存
3.通过viewContext接收实体
4.属性值未更新为第1页(!)

中的状态

同时,如果在抓取p.3之前立即调用viewContext.refreshAllObjects(),则属性值将得到更新

2 个答案:

答案 0 :(得分:0)

这是预期的输出。

您要在后台上下文中保存实体。背景上下文是来自持久性容器的上下文。方法save告诉我们以下信息:

  

尝试将未保存的对注册对象的更改提交给   上下文的父店。

  • backgroundContext的父存储是什么? persistentContainer
  • viewContext的父存储是什么? persistentContainer

在viewContext上进行查询时,您将收到数据库中的所有值。当您在backgroundContext上调用save时,它将告诉上下文的父存储区(persistentContainer)要保存。保存后,这些值将在“真实数据库”中提交,而不仅在私有背景上下文中提交。

您可以说viewContext的特殊之处在于它查询最新的提交数据。如果您有2个私有背景上下文并将automaticallyMergesChangesFromParent设置为false,则它们不会合并回彼此的提交操作,因此,在私有背景上下文A中保存的查询对象将在私有背景下不可见上下文B。

如果将automaticallyMergesChangesFromParent设置为true,则将根据其他上下文的提交(如果它们源自同一持久性容器)来更新后台上下文

viewContext中不合并来自其他上下文的提交绝对是愚蠢的,我想您可以弄清楚为什么它是不可维护的。

答案 1 :(得分:0)

您似乎在混淆automaticallyMergesChangesFromParent或将上下文与获取合并在一起。

fetch始终与持久存储区保持联系,而不考虑上下文是否已与另一个合并。这就是获取在核心数据中的工作方式。本书(https://www.objc.io/books/core-data/)上有一节“避免获取请求”解释了同一件事。我的解释是:

  

“最大的性能违规者是获取请求。提取请求   必须遍历整个核心数据堆栈。根据API合约,   请求(即使它起源于托管对象上下文)   将查询文件系统中的SQLite存储。

     

因此,获取请求本来就很昂贵。”

     

节选自:Florian Kugler。 “核心数据”。

第25页的另一个内容,获取请求

  

“我们现在要指出的一件事是:每次您   执行获取请求,核心数据将遍历完整的核心数据   堆栈,一直到文件系统。根据合同,提取请求是   往返:从上下文到持久存储   协调器和持久性存储,直到SQLite,然后是所有   回来。”

     

节选自:Florian Kugler。 “核心数据”。

这就是为什么即使您关闭了automaticallyMergesChangesFromParent,提取操作仍会从数据库中读取最新的值。

2015年和2016年WWDC关于核心数据的会议很好(嗯,它们都是),我建议您仔细阅读。在过去的五年左右的时间里,几乎没有六次会议。通过观看这些会议的最佳实践以及这些年来添加到Core Data中的增量更改,我学到了很多东西。

如果您出于任何原因希望继续查看数据的旧快照(也许您的背景上下文正在不断添加/删除条目,并且您还没有准备好刷新视图上下文),那么建议您看一下查询生成。它应该给您您想要达到的目标。