提取到 2 个不同上下文中的 NSManagedObject 具有不同的属性值
NSManagedObject fetched into 2 different contexts has different attribute values
我的应用程序有一个可重现的 CoreData 错误。
我使用 viewContext
进行显示,使用 backgroundContext
进行对象更新。两个上下文属于相同的 NSPersistentCloudKitContainer
.
在某些时候,我将对象的 status
属性从 2 更新为 1,并将其 updatedAt
属性从 nil
更新为 Date()
,然后将其保存在 backgroundContext
中。
后来,我想取回这个更新后的对象,我的理解是,一次取回总是returns持久存储的内容。
因此,无论将其提取到哪个上下文中,提取的对象都应该相同。然而,事实并非如此。
我还将 -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 的值与保存之前的值相同。
我的问题:
我的假设错了吗?我的代码有问题吗?
Later, I want to fetch back this updated object, and my understanding
is that a fetch always returns the content of the persistent store.
获取选择对象return基于持久存储的内容,但默认情况下不会更新 基于商店内容的对象的内存副本。有 可以做到这一点,根据我的经验,这是行不通的。要更新存储中的现有对象,您可以刷新它或在您的上下文中设置合并,以便自动传播对存储的更改。
我的应用程序有一个可重现的 CoreData 错误。
我使用 viewContext
进行显示,使用 backgroundContext
进行对象更新。两个上下文属于相同的 NSPersistentCloudKitContainer
.
在某些时候,我将对象的 status
属性从 2 更新为 1,并将其 updatedAt
属性从 nil
更新为 Date()
,然后将其保存在 backgroundContext
中。
后来,我想取回这个更新后的对象,我的理解是,一次取回总是returns持久存储的内容。
因此,无论将其提取到哪个上下文中,提取的对象都应该相同。然而,事实并非如此。
我还将 -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 的值与保存之前的值相同。
我的问题:
我的假设错了吗?我的代码有问题吗?
Later, I want to fetch back this updated object, and my understanding is that a fetch always returns the content of the persistent store.
获取选择对象return基于持久存储的内容,但默认情况下不会更新 基于商店内容的对象的内存副本。有