私のiPhoneアプリはコアデータストアを移行する必要があり、一部のデータベースは非常に大きいです。 Appleのドキュメントでは、「複数のパス」を使用してデータを移行し、メモリ使用量を削減することを推奨しています。 ただし、ドキュメントは非常に限られており、実際の実行方法についてはあまり説明されていません。誰かが良い例を示してくれたり、これを実際に実行するプロセスを詳細に説明したりできますか?
documentation にあるAppleヒントを理解しました。実際には非常に簡単ですが、それが明らかになるまでには長い道のりがあります。最初の状況は次のとおりです。
「コアデータストレージを備えたナビゲーションベースのアプリ」テンプレートを使用してプロジェクトを作成するときに取得するモデルです。私はそれをコンパイルし、forループの助けを借りていくつかのハードヒットを行い、いくつかの異なる値を持つ約2kのエントリを作成しました。 NSDate値を持つ2.000イベントに進みます。
次に、次のようなデータモデルの2番目のバージョンを追加します。
違いは次のとおりです。イベントエンティティはなくなり、2つの新しいエンティティがあります。タイムスタンプをdouble
として保存するものと、日付をNSString
として保存するものです。
目標は、すべてのバージョン1イベントを2つの新しいエンティティに転送し、移行に沿って値を変換することです。これにより、個別のエンティティの異なるタイプとしてそれぞれ2倍の値になります。
移行するには、手動で移行を選択します。これはマッピングモデルで行います。これは、あなたの質問に対する答えの最初の部分でもあります。 2kエントリの移行には時間がかかり、メモリフットプリントを低く抑えたいため、移行は2ステップで行います。
さらに、これらのマッピングモデルをさらに分割して、エンティティの範囲のみを移行することもできます。 100万件のレコードを取得したとします。これによりプロセス全体がクラッシュする可能性があります。 Filter predicate を使用して、取得したエンティティを絞り込むことができます。
次のような最初のマッピングモデルを作成します。
1。新規ファイル->リソース->マッピングモデル
2。名前を選択して、StepOneを選択しました
。ソースおよび宛先データモデルの設定
マルチパス移行では、カスタムエンティティ移行ポリシーは必要ありませんが、この例ではもう少し詳しく説明します。そのため、エンティティにカスタムポリシーを追加します。これは常に NSEntityMigrationPolicy
のサブクラスです。
このポリシークラスは、移行を実現するためのメソッドをいくつか実装しています。ただし、この場合は単純なので、1つのメソッドのみを実装する必要があります: createDestinationInstancesForSourceInstance:entityMapping:manager:error:
。
実装は次のようになります。
#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;
}
NSDateをdoubleに変換するために使用されるtimeIntervalSince1970だけで、ほぼ同一の2番目のマッピングモデルを設定する部分はスキップします。
最後に、移行をトリガーする必要があります。今のところ、定型コードをスキップします。必要な場合は、ここに投稿します。 移行プロセスのカスタマイズ にあります。これは、最初の2つのコード例の単なるマージです。 3番目と最後の部分は、次のように変更されます。 NSMappingModel
クラスのクラスメソッドを使用する代わりに mappingModelFromBundles:forSourceModel:destinationModel:
を使用します initWithContentsOfURL:
クラスメソッドは、バンドル内で見つかったマッピングモデルを1つだけ(おそらく最初に)返すためです。
これで、ループのすべてのパスで使用でき、移行メソッドを移行マネージャーに送信できる2つのマッピングモデルができました。それでおしまい。
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
で終わります。
宛先ストアを提供する必要があり、ソースストアであってはなりません。移行が成功したら、古いものを削除して新しいものの名前を変更できます。
マッピングモデルの作成後にデータモデルにいくつかの変更を加えたため、いくつかの互換性エラーが発生しましたが、マッピングモデルを再作成することでしか解決できませんでした。
これらの質問は関連しています:
iPhoneでの大きなCoreDataデータストアの移行に関するメモリの問題
最初のリンクを引用するには:
これについては、「複数パス」セクションの公式ドキュメントで説明されていますが、推奨されるアプローチは、エンティティタイプごとに移行を分割する、つまり、複数のマッピングモデルを作成し、それぞれが完全なデータモデル。