web-dev-qa-db-ja.com

複数のパスを使用したコアデータ移行の例または説明

私のiPhoneアプリはコアデータストアを移行する必要があり、一部のデータベースは非常に大きいです。 Appleのドキュメントでは、「複数のパス」を使用してデータを移行し、メモリ使用量を削減することを推奨しています。 ただし、ドキュメントは非常に限られており、実際の実行方法についてはあまり説明されていません。誰かが良い例を示してくれたり、これを実際に実行するプロセスを詳細に説明したりできますか?

84
Jason

documentation にあるAppleヒントを理解しました。実際には非常に簡単ですが、それが明らかになるまでには長い道のりがあります。最初の状況は次のとおりです。

データモデルバージョン1

enter image description hereenter image description here

「コアデータストレージを備えたナビゲーションベースのアプリ」テンプレートを使用してプロジェクトを作成するときに取得するモデルです。私はそれをコンパイルし、forループの助けを借りていくつかのハードヒットを行い、いくつかの異なる値を持つ約2kのエントリを作成しました。 NSDate値を持つ2.000イベントに進みます。

次に、次のようなデータモデルの2番目のバージョンを追加します。

enter image description here

データモデルバージョン2

違いは次のとおりです。イベントエンティティはなくなり、2つの新しいエンティティがあります。タイムスタンプをdoubleとして保存するものと、日付をNSStringとして保存するものです。

目標は、すべてのバージョン1イベントを2つの新しいエンティティに転送し、移行に沿って値を変換することです。これにより、個別のエンティティの異なるタイプとしてそれぞれ2倍の値になります。

移行するには、手動で移行を選択します。これはマッピングモデルで行います。これは、あなたの質問に対する答えの最初の部分でもあります。 2kエントリの移行には時間がかかり、メモリフットプリントを低く抑えたいため、移行は2ステップで行います。

さらに、これらのマッピングモデルをさらに分割して、エンティティの範囲のみを移行することもできます。 100万件のレコードを取得したとします。これによりプロセス全体がクラッシュする可能性があります。 Filter predicate を使用して、取得したエンティティを絞り込むことができます。

2つのマッピングモデルに戻ります。

次のような最初のマッピングモデルを作成します。

1。新規ファイル->リソース->マッピングモデル enter image description here

2。名前を選択して、StepOneを選択しました

。ソースおよび宛先データモデルの設定

enter image description here

マッピングモデルステップ1

enter image description here

enter image description here

enter image description here

マルチパス移行では、カスタムエンティティ移行ポリシーは必要ありませんが、この例ではもう少し詳しく説明します。そのため、エンティティにカスタムポリシーを追加します。これは常に NSEntityMigrationPolicy のサブクラスです。

enter image description here

このポリシークラスは、移行を実現するためのメソッドをいくつか実装しています。ただし、この場合は単純なので、1つのメソッドのみを実装する必要があります: createDestinationInstancesForSourceInstance:entityMapping:manager:error:

実装は次のようになります。

StepOneEntityMigrationPolicy.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;
}

最終ステップ:移行自体

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で終わります。

  • 宛先ストアを提供する必要があり、ソースストアであってはなりません。移行が成功したら、古いものを削除して新しいものの名前を変更できます。

  • マッピングモデルの作成後にデータモデルにいくつかの変更を加えたため、いくつかの互換性エラーが発生しましたが、マッピングモデルを再作成することでしか解決できませんでした。

171
Nick Weaver

これらの質問は関連しています:

iPhoneでの大きなCoreDataデータストアの移行に関するメモリの問題

iOSを使用したチャンクでのマルチパスコアデータ移行

最初のリンクを引用するには:

これについては、「複数パス」セクションの公式ドキュメントで説明されていますが、推奨されるアプローチは、エンティティタイプごとに移行を分割する、つまり、複数のマッピングモデルを作成し、それぞれが完全なデータモデル。

3
occulus