CoreData + mogenerator - 如何防止中间数据模型中的`setValue(forKey :)`引用最终数据模型中实体的'人类'类?

Posted

技术标签:

【中文标题】CoreData + mogenerator - 如何防止中间数据模型中的`setValue(forKey :)`引用最终数据模型中实体的\'人类\'类?【英文标题】:CoreData+mogenerator - How to prevent `setValue(forKey:)` in intermediate datamodel to refer to the 'human' class for entity in final datamodel?CoreData + mogenerator - 如何防止中间数据模型中的`setValue(forKey :)`引用最终数据模型中实体的'人类'类? 【发布时间】:2017-04-25 18:37:47 【问题描述】:

我将 Core Data 与 mogenerator 结合使用来管理一个相当大且链接紧密的数据对象图。

由于过去一些不幸的设计决策(将数据存储为Transformable 在对象中),我在执行迁移时遇到了内存问题;迁移非常困难,以至于轻量级无法涵盖它,并且自定义迁移尝试将所有内容加载到内存中并惨遭失败。

基于 Marcus Zarra 的出色 Core Data book,我调整了他的渐进式迁移方法,以便能够根据轻量级、自定义或“编写自己的”迁移策略混合匹配连续的迁移过程。我正在使用它来创建一个中间数据模型,在该模型中我加载我的“大数据”对象并将其写入磁盘上的外部文件,然后只保留该文件的 url。

基本上是这样的:

     v1   ----(lightweight)---------->  v1.5   --(lightweight)-->   v2
                         |                             |
* myData: Transformable  |  * myData: Transformable?   | * myDataUrl: String
                         |  * myDataUrl: String        |

在两次轻量级迁移之间,我将NSPersistentStoreController 连接到中间模型,使用fetchLimitfetchBatchSize 获取需要更改的对象;将数据写入磁盘上的文件并取消存储在对象本身中的数据,在此期间我会定期保存 moc 并对处理的对象进行故障处理。

这工作得相当好..但是......迁移的另一部分工作得不是很好,我已经删除了一个关系并将其替换为 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 上)

这让我吃惊,我特别用&lt;NSManagedObject&gt; 构建了NSFetchRequest,希望“切掉”MyEntity 类的底层动态逻辑,但似乎 Core Data/Swift 推断运行时类型基于实体名称/描述。有什么办法可以规避吗?

我也尝试使用 setPrimitiveValue,但这会导致我的 moc 错过更改我对对象所做的更改不会以这种方式保存。

【问题讨论】:

把这一切写下来有助于在某种程度上看到光明。我设法通过将中间数据模型中的myRel 重命名为myRelOld 并在中间转换中访问那个来解决这个问题。由于在迁移到数据模型 v2 时删除了该关系,因此这无关紧要。不过,如果有人能告诉我为什么 Core Data 试图使用“当前数据模型”实体而不是中间实体;请注意,我在最新模型中的计算属性未标记为@dynamic/@NSManaged;或者只是对我正在做的事情有新的认识,随时欢迎! 【参考方案1】:

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

【讨论】:

以上是关于CoreData + mogenerator - 如何防止中间数据模型中的`setValue(forKey :)`引用最终数据模型中实体的'人类'类?的主要内容,如果未能解决你的问题,请参考以下文章

iOS CoreData+MoGenerator:如何仅在使用嵌套上下文时初始化托管对象一次?

XCode 4.6 中的 Mogenerator 和 ARC

将 mogenerator 与 Core Data 实体一起使用会导致保存数据存储时出错

从 CoreData 关系中获取错误实体的对象

Mogenerator 是不是适用于 Xcode 6?

Simperium 和 mogenerator 便利访问器