Mic*_*sai 6 macos core-data macos-mojave
我正在尝试在一个上下文中更新和保存托管对象,然后在另一个上下文中访问更新的属性值。的文档shouldRefreshRefetchedObjects 说:
默认情况下,当您获取对象时,它们会保持其当前属性值,即使持久存储中的值已更改。使用参数 YES 调用此方法意味着在执行提取时,将使用持久存储中的当前值更新所提取对象的属性值。与依次对多个对象使用 refreshObject:mergeChanges: (NSManagedObjetContext) 相比,这是确保托管对象属性值与存储一致的更方便的方法。
所以我认为通过将其设置为true我可以在重新获取后获取当前值,而无需手动刷新单个对象。然而,情况似乎并非如此。在 macOS 10.14.5 上,获取请求将根据存储中的属性值选择合适的对象,但内存中的对象仍然具有陈旧的值。
下面是一些示例代码来说明问题。我希望它打印Old New New,但它打印Old Old New。
import Foundation
import CoreData
class Entity: NSManagedObject {
@NSManaged var attribute: String
}
let attribute = NSAttributeDescription()
attribute.name = "attribute"
attribute.attributeType = .stringAttributeType
let entityDescription = NSEntityDescription()
entityDescription.name = "Entity"
entityDescription.properties = [attribute]
entityDescription.managedObjectClassName = Entity.className()
let model = NSManagedObjectModel()
model.entities = [entityDescription]
let coordinator = NSPersistentStoreCoordinator(managedObjectModel: model)
try! coordinator.addPersistentStore(ofType: NSInMemoryStoreType, configurationName: nil, at: nil, options: [:])
let writeContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
writeContext.persistentStoreCoordinator = coordinator
let readContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
readContext.persistentStoreCoordinator = coordinator
let writeEntity = Entity(entity: entityDescription, insertInto: writeContext)
writeContext.performAndWait {
writeEntity.attribute = "Old"
try! writeContext.save()
}
var readEntity: Entity? = nil
readContext.performAndWait {
let request = NSFetchRequest<Entity>(entityName: entityDescription.name!)
readEntity = try! readContext.fetch(request).first!
// Initially the attribute should be Old, and that's what's printed
print(readEntity!.attribute)
}
writeContext.performAndWait {
writeEntity.attribute = "New"
try! writeContext.save()
}
readContext.performAndWait {
let request = NSFetchRequest<Entity>(entityName: entityDescription.name!)
request.shouldRefreshRefetchedObjects = true
_ = try! readContext.fetch(request)
// Now the attribute should be New, but it is still Old
print(readEntity!.attribute)
readContext.refresh(readEntity!, mergeChanges: false)
_ = try! readContext.fetch(request)
// However, manually refreshing and fetching again does update it to New
print(readEntity!.attribute)
}
Run Code Online (Sandbox Code Playgroud)
我知道refreshAllObjects(),但是:
shouldRefreshRefetchedObjects似乎正是我想要的;它似乎什么也没做。最好的解决方法似乎是单独刷新对象,但我猜这是低效的。
简短的回答是:这是框架中的一个错误。问题中的代码应该可以工作,但不能,因为它shouldRefreshRefetchedObjects不像宣传的那样工作。
我还尝试了代码的一些其他变体。我将其更改为使用 SQLite 持久存储,以便我可以打开 SQLite 调试并查看它是否告诉我任何有趣的事情。获取意外结果的 fetch 会在 Xcode 控制台中打印这些消息:
CoreData: annotation: with values: (
"<JunkCDCmd.Entity: 0x100704760> (entity: Entity; id: 0x9e9e44e129d3565d
<x-coredata://3F8EC946-9EF7-4A62-AEF2-A8670185E23C/Entity/p1>;
data: {\n attribute = Old;\n})"
)
Run Code Online (Sandbox Code Playgroud)
它正在变得Old,这不是这里所期望的。结果很可能来自托管对象上下文的缓存,这表明shouldRefreshRefetchedObjects框架没有检查该结果。
我还尝试了其他一些似乎不太有帮助的事情,但我不能不尝试就排除它们。我shouldRefreshRefetchedObjects在最初的获取中使用了,因为为什么不呢。我尝试避免readEntity第一次获取,而不是让它超出范围。我尝试将其嵌套writeContext.performAndWait在 内部readContext.performAndWait,以防范围相关。我将获取结果保存在变量中,而不是使用它_来确保结果不会意外地释放卡或其他东西。正如预期的那样,这些都没有任何区别。
赏金要求
彻底解释为什么实际上这不起作用,以及如何在它应该起作用的任何地方实施它。
如果没有框架的源代码,就不可能知道它不起作用的确切原因。如果没有的话,我们只能猜测。它明显坏了,而且看起来好像坏了,因为它没有检查shouldRefreshRefetchedObjects。如果它不适合你(而且看起来它不太可能适合你),那么请向 Apple 提交错误并希望得到最好的结果。
至于如何实现,如果这个设置正是您所需要的,那么没有确切的替代方案。刷新值的选项包括(排名不分先后)
refresh(_:mergeChanges:)通过使用withfalse作为第二个参数,强制对象在获取之前成为错误,如问题中所示。如果您有一堆受影响的对象,这可能会很尴尬。refresh(_:mergeChanges:)使用withtrue作为第二个参数直接刷新对象。您无需重新获取。如果您有一堆受影响的对象,这可能会很尴尬。refreshAllObjects(),这具有问题中描述的缺点。NSManagedObjectContextDidSave(也称为Notification.Name.didSaveObjectsNotification)。| 归档时间: |
|
| 查看次数: |
261 次 |
| 最近记录: |