開発中のアプリでは、Core Data と sqlite バッキング ストアを使用してデータを保存しています。アプリのオブジェクト モデルは複雑です。また、アプリによって提供されるデータの総量が大きすぎて、iOS (iPhone/iPad/iPod Touch) アプリ バンドルに収まりません。通常、ユーザーはデータのサブセットのみに関心があるため、アプリがデータ オブジェクトのサブセット (最大 100 MB) を含むようにデータを分割しました。アプリバンドル。ユーザーは、iTunes のアプリ内購入を通じて追加コンテンツの料金を支払った後、サーバーから追加のデータ オブジェクト (サイズが ~5 MB から 100 MB) をダウンロードするオプションがあります。増分データ ファイル (sqlite バッキング ストアに存在する) は、バンドルに同梱されるデータと同じ xcdatamodel バージョンを使用します。オブジェクト モデルへの変更はありません。増分データ ファイルは、サーバーから gzip 圧縮された sqlite ファイルとしてダウンロードされます。増分コンテンツをアプリと一緒に出荷することで、アプリ バンドルを肥大化させたくありません。また、Web サービスを介したクエリに依存したくありません (複雑なデータ モデルのため)。サーバーからの増分 sqlite データのダウンロードをテストしました。ダウンロードしたデータ ストアをアプリの共有の persistentStoreCoordinator に追加できました。
{
NSError *error = nil;
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:defaultStoreURL options:options error:&error])
{
NSLog(@"Failed with error: %@", [error localizedDescription]);
abort();
}
// Check for the existence of incrementalStore
// Add incrementalStore
if (incrementalStoreExists) {
if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:incrementalStoreURL options:options error:&error])
{
NSLog(@"Add of incrementalStore failed with error: %@", [error localizedDescription]);
abort();
}
}
}
ただし、この方法で行うには 2 つの問題があります。
- データ フェッチの結果 (NSFetchResultController などを使用) は、defaultStoreURL のデータの末尾に追加された incrementalStoreURL のデータと共に表示されます。
- 一部のオブジェクトが複製されています。私たちのデータ モデルには、読み取り専用データを持つエンティティが多数あります。これらは、2 番目の persistentStore を persistentStoreCoordinator に追加すると複製されます。
理想的には、Core Data が 2 つの永続ストアからのオブジェクト グラフを 1 つにマージすることを望みます (データのダウンロード時に、2 つのストアからのデータ間に共有関係はありません)。また、重複したオブジェクトを削除したいと考えています。Web を検索すると、この回答やこの回答など、私たちが行っているのと同じことをしようとしている人々によるいくつかの質問が見つかりました。Core Data での大規模なデータ セットのインポートに関する Marcus Zarra のブログを読みました。しかし、これまで見てきた解決策はどれもうまくいきませんでした。インクリメンタル ストアからデフォルト ストアにデータを手動で読み取って保存することは、電話では非常に遅く、エラーが発生しやすいと考えられるため、行いたくありません。マージを行うより効率的な方法はありますか?
次のように手動移行を実装することで、問題の解決を試みました。ただし、マージを成功させることはできませんでした。上記の回答 1 と 2 で提案されている解決策については、明確ではありません。Marcus Zarra のブログでは、大規模なデータセットを iOS にインポートするプロジェクトの開始時に発生した問題のいくつかに対処しました。
{
NSError *error = nil;
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
NSMigrationManager *migrator = [[NSMigrationManager alloc] initWithSourceModel:__managedObjectModel destinationModel:__managedObjectModel];
if (![migrator migrateStoreFromURL:stateStoreURL
type:NSSQLiteStoreType
options:options
withMappingModel:nil
toDestinationURL:destinationStoreURL
destinationType:NSSQLiteStoreType
destinationOptions:nil
error:&error])
{
NSLog(@"%@", [error userInfo]);
abort();
}
}
回答 1 の作成者は、最終的にインクリメンタル ストアからデータを読み取り、デフォルト ストアに保存したようです。おそらく、記事 1 と 2 の両方で提案されている解決策を誤解している可能性があります。データのサイズによっては、増分データを手動で読み取って既定のストアに再挿入することができない場合があります。私の質問は、オブジェクト グラフを (同じ objectModel を持つ) 2 つの PersistentStore から取得して 1 つの PersistentStore にマージする最も効率的な方法は何ですか?
自動移行は、新しいエンティティ属性をオブジェクト グラフに追加したり、関係を変更したりするときに非常にうまく機能します。自動移行が行われるため、同様のデータを停止して再開するのに十分な回復力を持つ同じ永続ストアにマージする簡単なソリューションはありますか?