11

多対多の関係を使用してタグとメモをリンクするiPhoneアプリがあります。現在、これを実現するためにCore Dataの「関係」機能を使用していますが、代わりに結合テーブルの使用に移行したいと考えています。

これが私の課題です。古いモデルから結合テーブルモデルに移行したいので、そのデータ移行を実行する方法を理解する必要があります。

これを行う方法の良い例はありますか?

更新:ここで何が起こっているのかを助けるために、ここで質問を明確にしています:Simperiumを使用してアプリをサポートしてみたいのですが、Simperiumは多対多の関係をサポートしていません(!)。

私がやろうとしていることの例として、iPhoneCoreDataRecipesアプリを例として使用してみましょう。

私のCoreDataスキームは現在次のようになっています。 ここに画像の説明を入力してください

...そしてこれが私が移行しているものです: ここに画像の説明を入力してください

どうすれば一方から他方に移動し、データを持ち込むことができますか?

コアデータ移行に関するAppleのドキュメントはまばらであることがよく知られており、NSEntityMappingまたはNSMigrationManagerサブクラスを使用して作業を完了するための有用なウォークスルーは見当たりません。

4

2 に答える 2

28

基本的なプロセスは次のとおりです。

  1. データモデルのバージョン管理されたコピーを作成します。(モデルを選択し、[エディター]-> [モデルバージョンの追加]を選択します)

  2. データモデルの新しいコピーに変更を加えます

  3. 新しいデータモデルのコピーを現在のバージョンとしてマークします。(最上位のxcdatamodelアイテムをクリックし、ファイルインスペクターで、[バージョン管理されたデータモデル]セクションの[現在]エントリを、手順1で作成した新しいデータモデルに設定します。

  4. モデルオブジェクトを更新して、RecipeIngredientエンティティを追加します。また、RecipeエンティティとIngredientエンティティの成分とレシピの関係を、ステップ2でRecipeIngredientエンティティに対して作成した新しい関係に置き換えます。(両方のエンティティにこの関係が追加されます。私はrecipeIngredientsと呼びました)明らかに、古いコードで材料からレシピへの関係を作成する場合は常に、RecipeIngredientオブジェクトを作成する必要があります。しかし、それはこの回答の範囲を超えています。

  5. モデル間に新しいマッピングを追加します([ファイル]->[新しいファイル...]->([コアデータ]セクション)-> [マッピングモデル]。これにより、いくつかのマッピングが自動生成されます。RecipeToRecipe、IngredientToIngredient、およびRecipeIngredient。

  6. RecipeIngredientマッピングを削除します。また、RecipeToRecipeおよびIngredientToRecipe(またはステップ2でそれらを呼び出したもの)に対して提供されるrecipeIngredientリレーションマッピングを削除します。

  7. RecipeToRecipeマッピングをドラッグして、マッピングルールのリストの最後に配置します。(これは、レシピを移行するときに材料をリンクできるように、レシピの前に材料が確実に移行されるようにするために重要です。)移行は移行ルールリストの順序で行われます。

  8. RecipeToRecipeマッピング「DDCDRecipeMigrationPolicy」のカスタムポリシーを設定します(これにより、Recipesオブジェクトの自動移行がオーバーライドされ、マッピングロジックを実行できるフックが提供されます。

  9. レシピのNSEntityMigrationPolicyをサブクラス化してcreateDestinationInstancesForSourceInstanceをオーバーライドすることにより、DDCDRecipeMigrationPolicyを作成します(以下のコードを参照)。これは、レシピごとに1回呼び出されます。これにより、Recipeオブジェクトと、それをIngredientにリンクする関連するRecipeIngredientオブジェクトを作成できます。手順5でXcodeが自動作成したマッピングルールによって、Ingredientを自動移行します。

  10. 永続オブジェクトストア(おそらくAppDelegate)を作成する場合は常に、データモデルを自動移行するようにユーザーディクショナリを設定してください。

if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType 
      configuration:nil 
      URL:storeURL 
      options:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,  nil] 
      error:&error])
{
}

レシピのサブクラスNSEntityMigrationPolicy

#import <CoreData/CoreData.h>
@interface DDCDRecipeMigrationPolicy : NSEntityMigrationPolicy
@end

* DDCDRecipeMigrationPolicy.mのcreateDestinationInstancesForSourceInstanceをオーバーライドします*

- (BOOL)createDestinationInstancesForSourceInstance:(NSManagedObject *)sInstance entityMapping:(NSEntityMapping *)mapping manager:(NSMigrationManager *)manager error:(NSError **)error
{

    NSLog(@"createDestinationInstancesForSourceInstance : %@", sInstance.entity.name);

   //We have to create the recipe since we overrode this method. 
   //It's called once for each Recipe.  
    NSManagedObject *newRecipe = [NSEntityDescription insertNewObjectForEntityForName:@"Recipe" inManagedObjectContext:[manager destinationContext]];
    [newRecipe setValue:[sInstance valueForKey:@"name"] forKey:@"name"];
    [newRecipe setValue:[sInstance valueForKey:@"overview"] forKey:@"overview"];
    [newRecipe setValue:[sInstance valueForKey:@"instructions"] forKey:@"instructions"];

    for (NSManagedObject *oldIngredient in (NSSet *) [sInstance valueForKey:@"ingredients"])
    {
        NSFetchRequest *fetchByIngredientName = [NSFetchRequest fetchRequestWithEntityName:@"Ingredient"];
        fetchByIngredientName.predicate = [NSPredicate predicateWithFormat:@"name = %@",[oldIngredient valueForKey:@"name"]];

        //Find the Ingredient in the new Datamodel.  NOTE!!!  This only works if this is the second entity migrated.
         NSArray *newIngredientArray = [[manager destinationContext] executeFetchRequest:fetchByIngredientName error:error];

        if (newIngredientArray.count == 1)
        {
             //Create an intersection record. 
            NSManagedObject *newIngredient = [newIngredientArray objectAtIndex:0];
            NSManagedObject *newRecipeIngredient = [NSEntityDescription insertNewObjectForEntityForName:@"RecipeIngredient" inManagedObjectContext:[manager destinationContext]];
            [newRecipeIngredient setValue:newIngredient forKey:@"ingredient"];
            [newRecipeIngredient setValue:newRecipe forKey:@"recipe"];
             NSLog(@"Adding migrated Ingredient : %@ to New Recipe %@", [newIngredient valueForKey:@"name"], [newRecipe valueForKey:@"name"]);
        }


    }

    return YES;
}

XcodeとサンプルXcodeプロジェクトのセットアップの写真を投稿しますが、スタックオーバーフローに関するレピュテーションポイントがまだないようです...そのため、許可されません。これもブログに投稿します。bingosabi.wordpress.com/。

また、Xcode Core Dataモデルのマッピングは少し不安定であり、「クリーン」で優れたXcodeレスター、シミュレーターバウンス、または上記のすべてが機能する必要がある場合があることにも注意してください。

于 2012-07-02T23:13:16.760 に答える
1

質問へのコメントで示唆したように、データモデルを変更するのではなく、多対多の関係を理解し​​ないモデルとライブラリの間にブリッジを作成することをお勧めします。

作成する結合テーブルは実際にはすでに存在します。このライブラリにデータを表示する別の方法が必要です。

これが機能するかどうかは、このライブラリがモデルをどのように見るかによって異なります。エンティティのプロパティを照会するにはさまざまな方法があります。または、コピーするプロパティ/関係を指定するのはあなたである可能性があります。

これらすべての詳細がなければ、本当の答えを出すのは難しいですが、一般的な考え方は次のとおりです。

次のようなヘッダーを持つ管理対象オブジェクトがいくつかあります。

// Recipe.h

@interface Recipe : NSManagedObject
@property (nonatomic,retain) NSSet *ingredients;
@end

次に、カテゴリを使用して、このオブジェクトにいくつかの追加メソッドを追加します。

// Recipe+fakejoin.h

@interface Recipe (fakejoin)
-(NSSet*)recipeIngredients;
@end

そして、 withオブジェクトRecipe+fakejoin.mを返すこのメソッドの実装。NSSetRecipeIngredients

しかし、私が言ったように、このライブラリがあなたが物事を壊すことなくこのように遊ぶことを可能にするかどうかは未解決の質問です。これがすべてあなたにとって新しいように聞こえる場合は、別の解決策を見つけてください...

于 2012-06-29T01:41:19.583 に答える