我将 Core Data 与 mogenerator 结合使用来管理一个相当大且高度链接的数据对象图。
由于过去一些不幸的设计决策(将数据存储为Transformable
对象),我在执行迁移时遇到了内存问题;迁移非常困难,以至于轻量级无法涵盖它,并且自定义迁移尝试将所有内容加载到内存中并且惨遭失败。
基于 Marcus Zarra 的优秀Core Data 书籍,我调整了他的渐进式迁移方法,以便能够根据轻量级、自定义或“编写自己的”迁移策略混合和匹配连续的迁移过程。我正在使用它来创建一个中间数据模型,在该模型中我加载我的“大数据”对象并将其写入磁盘上的外部文件,然后只保留该文件的 url。
基本上,这看起来像这样:
v1 ----(lightweight)----------> v1.5 --(lightweight)--> v2
| |
* myData: Transformable | * myData: Transformable? | * myDataUrl: String
| * myDataUrl: String |
在两次轻量级迁移之间,我将 a连接到中间模型,使用,NSPersistentStoreController
获取需要更改的对象;将数据写入磁盘上的文件并取消存储在对象本身中的数据,在此期间我定期保存 moc 并对处理的对象进行故障排除。fetchLimit
fetchBatchSize
这工作得很好..但是......迁移的另一部分工作得不是很好,我已经删除了一个关系并将其替换为 mogenerated 文件的“人类”类中的计算属性。即
v1 ----(lightweight)----------> v1.5 --(lightweight)--> v2
| |
* myRel ->> [Some object] | * myRel ->> [Some object]? | (nothing stored here)
computed property `myRel` in the `MyEntity` human class
遵循相同的原则,在“v1.5-to-v1.5”传递中,我将存储的信息移动myRel
到不同的级别,并尝试将关系设置为nil
之后。使用数据模型 v2 的应用程序仍然myRel
使用相同的接口访问引用的对象,因为我已将其添加为检索移动数据的计算属性。
public var myRel: [Some object] { return ... }
这是执行此操作的代码:
// Note that since I'm not working on the current data model version in this
// so-called migration pass, I cannot/should not refer to the real `MyEntity` class that
// mogenerator generates for me.
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "MyEntity")
let objects = try? moc.fetch(fetchRequest)
objects.forEach { object in
// Process objects in `myRel` and move them to a different level
let toProcess = ($0.value(forKey: "myRel") as? NSOrderedSet)?.array as? [NSManagedObject]?
// ... process ...
// Now nullify the original relationship
$0.setValue(nil, forKey: "myRel")
}
最后一行导致运行时崩溃,显示一个堆栈跟踪,该堆栈跟踪导致为最终模型版本生成的“对象”文件生成器,而不是为此中间模型
#0 0x0000000100f6885e in MyEntity.myRel.getter at ...
#1 0x0000000100f68732 in @objc MyEntity.myRel.getter ()
#2 0x000000010ebf7db7 in _PF_Handler_Public_GetProperty ()
#3 0x000000010124f221 in NSKeyValueWillChangeBySetting ()
#4 0x0000000101249798 in NSKeyValueWillChange ()
#5 0x000000010121f618 in -[NSObject(NSKeyValueObserverNotification) willChangeValueForKey:] ()
#6 0x0000000100f47222 in NSManagedObject.setValue<A where ...> (Any?, for : A) -> () at ...
这自然会崩溃,因为我正在处理的数据模型不包含我的计算属性中引用的实体myRel
(这些实体仅存在于数据模型 v2 上)
这让我感到惊讶,我NSFetchRequest
特别构造了这个<NSManagedObject>
,希望“切掉”MyEntity
类的底层动态逻辑,但似乎 Core Data/Swift 会根据实体名称/描述推断运行时类型。有什么办法可以规避这种情况吗?
我也尝试过使用setPrimitiveValue
,但这会导致我的 moc 错过更改我对对象所做的更改不会以这种方式保存。