1. ホーム
  2. iphone

[解決済み] 複数パスによるCore Data Migrationの例と解説?

2023-05-11 05:42:24

質問

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

どのように解決するのですか?

私は、Apple が彼らの ドキュメント . 実はとても簡単なことなのですが、明白になるまでには長い道のりがあります。例を挙げて説明します。最初の状況はこうです。

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

これは、"コア データ ストレージ付きナビゲーション ベース アプリ" テンプレートでプロジェクトを作成したときに取得されるモデルです。これをコンパイルして、for ループの助けを借りて、異なる値を持つ約 2,000 のエントリを作成するために、いくつかのハードヒットを行ってみました。NSDate 値を持つ 2,000 個のイベントを作成しました。

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

データモデル 第二版

違うのは Eventエンティティがなくなり、新たに2つのエンティティが追加されました。一つは、タイムスタンプを double として保存するもの、そしてもう一つは日付を NSString .

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

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

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

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

最初のマッピングモデルはこのように作成します。

1. 新規ファイル -> リソース -> マッピングモデル

2. 名前を決めてください、私はStepOneを選びました

3. 送信元と送信先のデータモデルを設定する

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

マルチパス移行では、カスタムエンティティ移行ポリシーは必要ありませんが、この例ではもう少し詳細な情報を得るために、これを実行します。そこで、エンティティにカスタム ポリシーを追加します。これは常に NSEntityMigrationPolicy .

このポリシークラスは、マイグレーションを実現するためのいくつかのメソッドを実装しています。しかし、今回はシンプルなので、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;
}

最終段階:マイグレーションそのもの

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 をバンドルしています。

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

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