私の iPhone アプリはコア データ ストアを移行する必要があり、一部のデータベースは非常に大きいです。Apple のドキュメントでは、メモリ使用量を削減するために「複数のパス」を使用してデータを移行することを推奨しています。しかし、ドキュメントは非常に限られており、実際にこれを行う方法についてはあまりよく説明されていません。誰か良い例を教えてくれるか、実際にこれを実現するプロセスを詳しく説明してくれませんか?
ベストアンサー1
私はアップルが何を示唆しているのか理解したドキュメンテーション実はとても簡単ですが、それが明らかになるまでには長い道のりがあります。例を挙げて説明しましょう。最初の状況は次のようになります。
データモデル バージョン 1
これは、「コア データ ストレージを備えたナビゲーション ベースのアプリ」テンプレートを使用してプロジェクトを作成するときに取得するモデルです。これをコンパイルし、for ループを使用してハード ヒットを実行し、すべて異なる値を持つ約 2,000 個のエントリを作成しました。NSDate 値を持つ 2,000 件のイベントが作成されました。
ここで、次のようなデータ モデルの 2 番目のバージョンを追加します。
データモデル バージョン 2
違いは、Event エンティティがなくなり、新しいエンティティが 2 つあることです。1 つはタイムスタンプを として保存しdouble
、もう 1 つは日付を として保存しますNSString
。
目標は、すべてのバージョン 12 つの新しいエンティティにイベントを追加し、移行に沿って値を変換します。これにより、別々のエンティティに異なるタイプの値が 2 倍になります。
移行するには、手動での移行を選択し、これをマッピング モデルで行います。これは、質問に対する回答の最初の部分でもあります。2,000 エントリの移行には時間がかかり、メモリ フットプリントを低く抑えたいため、移行は 2 つのステップで行います。
これらのマッピングモデルをさらに分割して、エンティティの範囲のみを移行することもできます。たとえば、100万件のレコードがある場合、プロセス全体がクラッシュする可能性があります。取得するエンティティを絞り込むには、フィルター述語。
2 つのマッピング モデルに戻ります。
最初のマッピング モデルは次のように作成します。
1. 新規ファイル -> リソース -> マッピング モデル
2. 名前を選択します。私はStepOneを選択しました。
3. ソースと宛先のデータモデルを設定する
マッピングモデル ステップ 1
マルチパス移行ではカスタムエンティティ移行ポリシーは必要ありませんが、この例ではもう少し詳細にするためにそれを行います。エンティティにカスタムポリシーを追加します。これは常にNSEntityMigrationPolicy
。
このポリシー クラスは、移行を実現するためのいくつかのメソッドを実装します。ただし、この場合は単純なので、実装する必要があるメソッドは 1 つだけです。createDestinationInstancesForSourceInstance:entityMapping:manager:error:
。
実装は次のようになります。
ステップワンエンティティ移行ポリシー.m
#import "StepOneEntityMigrationPolicy.h"
@implementation StepOneEntityMigrationPolicy
- (BOOL)createDestinationInstancesForSourceInstance:(NSManagedObject *)sInstance
entityMapping:(NSEntityMapping *)mapping
manager:(NSMigrationManager *)manager
error:(NSError **)error
{
// Create a new object for the model context
NSManagedObject *newObject =
[NSEntityDescription insertNewObjectForEntityForName:[mapping destinationEntityName]
inManagedObjectContext:[manager destinationContext]];
// do our transfer of nsdate to nsstring
NSDate *date = [sInstance valueForKey:@"timeStamp"];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setTimeStyle:NSDateFormatterMediumStyle];
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
// set the value for our new object
[newObject setValue:[dateFormatter stringFromDate:date] forKey:@"printedDate"];
[dateFormatter release];
// do the coupling of old and new
[manager associateSourceInstance:sInstance withDestinationInstance:newObject forEntityMapping:mapping];
return YES;
}
最終ステップ:移行そのもの
2 番目のマッピング モデルを設定する部分は省略します。この部分はほぼ同じで、NSDate を double に変換するために使用される timeIntervalSince1970 だけです。
最後に、移行を開始する必要があります。定型コードはここでは省略します。必要な場合は、ここに投稿します。次の場所にあります。移行プロセスのカスタマイズこれは最初の2つのコード例をマージしただけです。3番目と最後の部分は次のように変更されます。NSMappingModel
クラスmappingModelFromBundles:forSourceModel:destinationModel:
私たちはinitWithContentsOfURL:
クラス メソッドは、バンドル内で見つかったマッピング モデルを 1 つだけ (おそらく最初に見つかったもの) 返すためです。
これで、ループの各パスで使用できる 2 つのマッピング モデルができました。移行マネージャーに migrate メソッドを送信します。これで完了です。
NSArray *mappingModelNames = [NSArray arrayWithObjects:@"StepOne", @"StepTwo", nil];
NSDictionary *sourceStoreOptions = nil;
NSURL *destinationStoreURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"CoreDataMigrationNew.sqlite"];
NSString *destinationStoreType = NSSQLiteStoreType;
NSDictionary *destinationStoreOptions = nil;
for (NSString *mappingModelName in mappingModelNames) {
NSURL *fileURL = [[NSBundle mainBundle] URLForResource:mappingModelName withExtension:@"cdm"];
NSMappingModel *mappingModel = [[NSMappingModel alloc] initWithContentsOfURL:fileURL];
BOOL ok = [migrationManager migrateStoreFromURL:sourceStoreURL
type:sourceStoreType
options:sourceStoreOptions
withMappingModel:mappingModel
toDestinationURL:destinationStoreURL
destinationType:destinationStoreType
destinationOptions:destinationStoreOptions
error:&error2];
[mappingModel release];
}
ノート
マッピング モデルは
cdm
バンドル内で終了します。移行先ストアを指定する必要があり、ソース ストアであってはなりません。移行が成功したら、古いストアを削除し、新しいストアの名前を変更できます。
マッピング モデルの作成後にデータ モデルにいくつか変更を加えたところ、互換性エラーが発生しました。このエラーは、マッピング モデルを再作成することによってのみ解決できました。