提取到 2 个不同上下文中的 NSManagedObject 具有不同的属性值

Posted

技术标签:

【中文标题】提取到 2 个不同上下文中的 NSManagedObject 具有不同的属性值【英文标题】:NSManagedObject fetched into 2 different contexts has different attribute values 【发布时间】:2021-01-24 13:53:51 【问题描述】:

我的应用存在可重现的 CoreData 错误。 我使用viewContext 进行显示,使用backgroundContext 进行对象更新。两个上下文都属于同一个NSPersistentCloudKitContainer。 在某个时候,我将一个对象的 status 属性从 2 更新为 1,并将其 updatedAt 属性从 nil 更新为 Date() 后,将其保存在 backgroundContext 中。 后来想取回这个更新的对象,我的理解是一次取回总是返回持久化存储的内容。 因此,无论将其提取到哪个上下文中,获取的对象都应该是相同的。但是,事实并非如此。 我还将-com.apple.CoreData.ConcurrencyDebug 1 设置为启动参数,所以这不是CoreData 多线程错误。 这是我的测试代码:

对象保存在这里:

let context = backgroundContext!
context.performAndWait 
    assert(ItemStatus(rawValue: item.status) == .isBought)
    item.status = ItemStatus.isToBuy.rawValue
    item.updatedAt = Date()
    _ = saveContext(context)
  

func saveContext(_ context: NSManagedObjectContext) -> Error? 
    if !context.hasChanges  return nil 
    let inserts = context.insertedObjects; if !inserts.isEmpty  print("Will save inserted objects: \(inserts)") 
    let updates = context.updatedObjects;  if !updates.isEmpty  print("Will save updated objects: \(updates)") 
    let deletes = context.deletedObjects;  if !deletes.isEmpty  print("Will save deleted objects: \(deletes)") 
    do 
        try context.save()
        print("\(context.name!) saved")
     catch 
        fatalError("Unresolved error")
    
    return nil
  

稍后,我使用以下方法将对象提取到两个上下文中:

let mwFetchRequest = NSFetchRequest<Item>(entityName: Item.entityName)
let passwordPredicate = NSPredicate(format: "\(Schema.Item.password) == %@", password)
let namePredicate = NSPredicate(format: "\(Schema.Item.name) == %@", "Mineral water")
let compoundPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [passwordPredicate, namePredicate])
mwFetchRequest.predicate = compoundPredicate
mwFetchRequest.returnsObjectsAsFaults = false

backgroundContext.performAndWait 
    let bcItem = try! backgroundContext.fetch(mwFetchRequest)
    print("backgroundContext: \(bcItem)")


viewContext.performAndWait 
    let vcItem = try! viewContext.fetch(mwFetchRequest)
    print(„viewContext: \(vcItem)")
  

这是我在这段代码之后设置断点时的日志:

Will save updated objects: [<ShopEasy.Item: 0x600000d7cf50> (entity: Item; id: 0x9698d776a7665623 <x-coredata://35BF43D6-4CF7-490D-B944-9DDFF2823AA1/Item/p1617>; data: 
    buyPlaces = "<relationship fault: 0x600002e296a0 'buyPlaces'>";
    fixedAtTopAt = nil;
    howOftenBought = 1;
    lastBoughtDate = "2021-01-24 13:02:09 +0000";
    name = "Mineral water";
    password = "PW_1";
    status = 1;
    updatedAt = "2021-01-24 13:32:14 +0000";
)]
backgroundContext saved
…
backgroundContext: [<ShopEasy.Item: 0x600000d7cf50> (entity: Item; id: 0x9698d776a7665623 <x-coredata://35BF43D6-4CF7-490D-B944-9DDFF2823AA1/Item/p1617>; data: 
    buyPlaces = "<relationship fault: 0x600002e296a0 'buyPlaces'>";
    fixedAtTopAt = nil;
    howOftenBought = 1;
    lastBoughtDate = "2021-01-24 13:02:09 +0000";
    name = "Mineral water";
    password = "PW_1";
    status = 1;
    updatedAt = "2021-01-24 13:32:14 +0000";
)]
viewContext: [<ShopEasy.Item: 0x600000d75ae0> (entity: Item; id: 0x9698d776a7665623 <x-coredata://35BF43D6-4CF7-490D-B944-9DDFF2823AA1/Item/p1617>; data: 
    buyPlaces =     (
        "0x9698d776b10e5621 <x-coredata://35BF43D6-4CF7-490D-B944-9DDFF2823AA1/Place/p971>"
    );
    fixedAtTopAt = nil;
    howOftenBought = 1;
    lastBoughtDate = "2021-01-24 13:02:09 +0000";
    name = "Mineral water";
    password = "PW_1";
    status = 2;
    updatedAt = nil;
)]  

显然,对象首先使用backgroundContext 正确保存,因此应该在持久存储中。 然后它被正确地取回backgroundContext。 但是在将同一个对象提取到viewContext 之后,两个更改的属性status 和updatedAt 的值与保存之前的值相同。

我的问题: 我的假设是错误的吗?我的代码有问题吗?

【问题讨论】:

我认为您的理解是错误的-来自文档:“如果您获取一些对象,使用它们,然后执行包含这些对象的超集的新获取,您不会获得新实例或更新现有对象的数据——您将获得具有当前内存状态的现有对象。” 【参考方案1】:

稍后,我想取回这个更新的对象,以及我的理解 是 fetch 总是返回持久存储的内容。

获取选择基于持久存储的内容返回的对象,但默认情况下它不会更新基于对象的内存副本商店的内容。有an option 可以做到这一点,根据我的经验,这是行不通的。要从存储中更新现有对象,您可以刷新它或在上下文中设置合并,以便自动传播对存储的更改。

【讨论】:

以上是关于提取到 2 个不同上下文中的 NSManagedObject 具有不同的属性值的主要内容,如果未能解决你的问题,请参考以下文章

Core Data 使用多个上下文中的新对象从后台线程订购一对多关系保存

LINQPad,使用多个datacontexts

逻辑应用程序中的XPath根据其类型提取值

用VBA怎么把一个表格中的数据不重复提取到另一个表格中

帮助从 s-s-rS 2008 中的不同数据库服务器中提取数据

使用 RegEx 搜索术语并提取其前后的字符 [重复]