0

我将 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 并对处理的对象进行故障排除。fetchLimitfetchBatchSize

这工作得很好..但是......迁移的另一部分工作得不是很好,我已经删除了一个关系并将其替换为 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 错过更改我对对象所做的更改不会以这种方式保存。

4

1 回答 1

0

把这一切写下来有助于在某种程度上看到光明。myRel我设法通过在中间数据模型中重命名myRelOld并在中间转换中访问该模型来解决该问题。由于在迁移到数据模型 v2 时删除了该关系,因此这无关紧要。

于 2017-04-26T05:58:59.807 回答