Swift 合并不同的上下文';s数据,自动从父级使用中删除

Swift 合并不同的上下文';s数据,自动从父级使用中删除,swift,core-data,Swift,Core Data,我有一个使用CoreData的应用程序,它有几个背景上下文(NSManagedObjectContext) 在编写一些测试时,我观察到一种奇怪的行为,这种行为似乎与官方文件存在争议: 来自一个上下文的更改将自动传播到另一个上下文,而在这两个上下文中。automaticallyMergesChangesFromParent都设置为false 这两个上下文都是从NSPersistentContainer接收的-一个来自.viewContext,另一个来自.newBackgroundContext()

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

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

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

如上所述,在上下文保存时,更改会提交到上下文的父存储,即NSPersistentContainer。
但事实上,更改也出现在另一个上下文中,尽管事实上automaticallyMergesChangesFromParent==false(这是一个默认值)

let persistentContainer=NSPersistentContainer(名称:“测试”)
让mainContext=persistentContainer.viewContext
让otherContext=persistentContainer.newBackgroundContext()
//测试实体是在另一个上下文上创建的
让实体:TestEntity=NSEntityDescription.insertNewObject
(forEntityName:String(描述:TestEntity.self),
进入:另一个上下文)as!测试性
entity.statusCode=“测试”
//准备测试实体的获取请求
让fetchReq:NSFetchRequest=TestEntity.fetchRequest()
fetchReq.predicate=NSPredicate(格式:“statusCode=%@”,
argumentArray:[“测试”])
//确保实体不存在于mainContext中
让entityFromMain=试试!mainContext.fetch(fetchReq)
XCTASERTEQUAL(entityFromMain.count,0)
//保存具有实体的上下文
尝试otherContext.save()
//确保父存储中的更改不会自动合并
XCTAssertFalse(mainContext.automaticallyMergesChangesFromParent)
//从mainContext获取插入的实体
让EntityOnMainAfterSave=试试!mainContext.fetch(fetchReq)
//实体存在于mainContext中
XCTAssertTrue(EntityOnMainAfterSave.count>0)
预期输出-EntityOnMainAfterSaveing不应包含新创建的实体,但它已经存在,尽管mainContext未刷新

更新: 我这样问是因为在我的应用程序中有一种情况:
1.实体的属性在其他上下文中更改
2.已保存其他上下文
3.实体是通过viewContext接收的
4.属性的值未更新为第1页中的状态(!)


同时,如果在获取第3页之前调用viewContext.refreshAllObjects(),则属性的值将得到更新。这是预期的输出

您正在后台上下文中保存实体。背景上下文是来自持久容器的上下文。该方法告诉我们以下内容:

尝试将注册对象的未保存更改提交到 上下文的父存储

  • backgroundContext
    的父存储是什么<代码>持久化容器
  • viewContext
    的父存储是什么<代码>持久化容器
在viewContext上查询时,将收到数据库中的所有值。当您在
backgroundContext
上调用save时,它将告诉上下文的父存储(
persistentcainer
)它要保存。保存后,值将在“真实数据库”中提交,而不仅仅是在私有后台上下文中提交

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

如果将来自父对象的
automaticallyMergesChangesFromParent
设置为
true
,则后台上下文将从其他上下文的提交中更新(如果它们来自相同的持久容器)


viewContext
中不合并来自其他上下文的提交绝对是愚蠢的,我想您可以找出原因,它是不可维护的。

您似乎混淆了
automaticallyMergesChangesFromParent
或合并上下文与获取

无论上下文是否与另一个上下文合并,
fetch
将始终到达持久存储。这就是核心数据中回迁的工作方式。这本书()中有一节是关于“避免获取请求”的,解释了同样的事情。我的意思是:

“影响性能的最大因素是获取请求。获取请求 必须遍历整个核心数据堆栈 请求-即使它起源于托管对象上下文- 将查阅文件系统中的SQLite存储

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

摘自:弗洛里安·库格勒。“核心数据。”

第25页,获取请求:

“我们现在要指出的一件重要事情是:每次 执行提取请求,核心数据将通过完整的核心数据 堆栈,一直到文件系统 往返:从上下文到持久存储 协调器和持久存储,一直到SQLite,然后是所有 很久以前。”

摘自:弗洛里安·库格勒。“核心数据。”

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

2015年和2016年关于核心数据的WWDC会议很好(嗯,它们都很好),我建议您仔细阅读。在过去5年左右的时间里,几乎没有6次会议。我从观看这些课程中学到了很多东西,因为他们回顾了最佳实践以及t
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)