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
连接到中间模型,使用fetchLimit
、fetchBatchSize
获取需要更改的对象;将数据写入磁盘上的文件并取消存储在对象本身中的数据,在此期间我会定期保存 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 上)
这让我吃惊,我特别用<NSManagedObject>
构建了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