Core Data を mogenerator と組み合わせて使用して、かなり大きくリンクの多いデータ オブジェクト グラフを管理しています。
過去の不運な設計上の決定 (データをTransformable
オブジェクトとして格納する) が原因で、移行の実行時にメモリの問題が発生しています。移行は非常に難しいため、軽量ではカバーできず、カスタム移行はすべてをメモリにロードしようとしますが、惨めに失敗します。
Marcus Zarra による優れたCore Data bookに基づいて、軽量、カスタム、または「独自の」移行戦略に従って、連続する移行パスを組み合わせることができるように、彼の漸進的な移行アプローチを適応させました。これを使用して、「ビッグデータ」オブジェクトをロードしてディスク上の外部ファイルに書き出す中間データモデルを作成し、代わりにそのファイルへの URL を保持します。
基本的に、これは次のようになります。
v1 ----(lightweight)----------> v1.5 --(lightweight)--> v2
| |
* myData: Transformable | * myData: Transformable? | * myDataUrl: String
| * myDataUrl: String |
2 つの軽量移行の間に、 を中間モデルに接続し、をNSPersistentStoreController
使用して変更が必要なオブジェクトをフェッチします。データをディスク上のファイルに書き込み、オブジェクト自体に保存されているデータを無効にします。その間、私は定期的にモックを保存し、処理されたオブジェクトをフォールトします。fetchLimit
fetchBatchSize
これはかなりうまく機能します..しかし...移行の別の部分がうまく機能していません。そこでは、関係を削除し、それを移動生成されたファイルの「人間」クラスの計算されたプロパティに置き換えました。
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")
}
この最後の行によりランタイム クラッシュが発生し、この中間モデルではなく、最終モデル バージョン用に生成された mogenerator の「オブジェクト」ファイルにつながるスタック トレースが表示されます。
#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
ましたが、それによりモックが変更を見逃してしまい、オブジェクトに加えた変更がそのように保存されません。