27

私のアプリは、必ずしも保存する必要のないオブジェクトをマネージド オブジェクト コンテキストに挿入することがあります。たとえば、「エンティティの追加」モーダルを起動すると、マネージド オブジェクトを作成してモーダルに割り当てます。ユーザーがそのモーダルから保存すると、コンテキストが保存されます。彼がキャンセルした場合、私はオブジェクトを削除し、保存は必要ありません。

(URL スキームを使用して) アプリに切り替えてエンティティを追加する「インポート」機能を導入しました。これらのモーダルの 1 つが開いている可能性があるため、この時点でコンテキストを保存するのは安全ではありません。モーダル用に作成された一時オブジェクトは、ユーザーがキャンセルしても保存されます。(キャンセル操作による) 削除が後で保存されるという保証はありません。ユーザーがアプリを終了する可能性があります。

同様に、アプリが終了するたびに単純に保存することはできません。その時点でモーダルが開いていると、一時オブジェクトが正しく保存されません。

これに対処するために、ここで説明したように、子コンテキストを使用しようとしています。SOで見つけたものをすべて読んだので、いくつか質問があります。

  1. 各コンテキストにどの同時実行タイプを使用する必要がありますか? パフォーマンス/スレッドの利点のためにこれを行っているわけではないことに注意してください。子コンテキストを持つ場合、メイン コンテキストに NSConfinementConcurrencyType を使用できないことはわかっていますが、他の 2 つのオプションのどちらが最適かはわかりません。子コンテキストの場合、一致する必要がありますか? それとも、ここで監禁タイプを使用できますか?さまざまな組み合わせを試してみましたが、すべて問題なく動作しているように見えますが、自分の要件に適したものを知りたいです。

  2. (副次的な問題) クラス iVar を使用する場合にのみ、これを機能させることができるのはなぜですか? 作成されたメソッドで一時的なコンテキストを宣言し、後で entity.managedObjectContext を使用して参照できるようにすべきだと考えました。でもアクセスしてくる頃には無くなっているような?代わりに iVar を使用して参照を保持すると、これは修正されます。

  3. メインコンテキストへの変更を反映する正しい方法は何ですか? 各コンテキストで異なるブロックラップ実装を使用するさまざまなコメントを見てきました。同時実行タイプに依存しますか? 私の現在のバージョンは次のとおりです。

    //save the new entity in the temporary context
    NSError *error = nil;
    if (![myObject.managedObjectContext save:&error]) {NSLog(@"Error - unable to save new object in its (temporary) context");}
    
    //propogate the save to the main context
    [self.mainContext performBlock:^{
        NSError *error2 = nil;
        if (![self.mainContext save:&error2]) {NSLog(@"Error - unable to merge new entity into main context");}
    }];
    
  4. ユーザーが保存すると、デリゲート (メイン ビュー コントローラー) にメッセージが送信されます。デリゲートには、追加されたオブジェクトが渡され、メイン コンテキストで同じオブジェクトを見つける必要があります。しかし、メイン コンテキストで検索しても見つかりません。メイン コンテキストにはエンティティ含まれています - 詳細をログに記録し、そこに存在することを確認できます - しかし、アドレスは異なりますか? これが意図されている場合 (なぜですか?)、保存後に追加されたオブジェクトをメイン コンテキストで見つけるにはどうすればよいですか?

洞察をありがとう。長くて複数の部分からなる質問で申し訳ありませんが、誰かが以前にこれらすべての問題に対処した可能性が高いと思いました。

4

3 に答える 3

47

親/子 MOC モデルは、Core Data の非常に強力な機能です。これにより、対処しなければならなかった長年の並行性の問題が驚くほど単純化されます。ただし、あなたが述べたように、並行性はあなたの問題ではありません。質問に答えるには:

  1. 伝統的に、メイン スレッドに関連付けられたNSMainQueueConcurrencyTypeにはを使用し、子コンテキストには を使用します。子コンテキストは、その親と一致する必要はありません。タイプを指定しない場合、これがすべてのデフォルトになります。基本的に「Core Dataのスレッドは自分で管理する」タイプです。NSManagedObjectContextNSPrivateQueueConcurrencyTypeNSConfinementConcurrencyTypeNSManagedObjectContext
  2. あなたのコードを見なければ、子コンテキストを作成するスコープが終了し、クリーンアップされると思います。
  3. 親/子コンテキスト パターンを使用する場合は、ブロック メソッドを使用する必要があります。ブロック メソッドを使用する最大の利点は、OS がメソッド呼び出しを正しいスレッドにディスパッチすることです。performBlock非同期実行または同期実行に使用できますperformBlockAndWait

これを次のように使用します。

- (void)saveContexts {
    [childContext performBlock:^{
        NSError *childError = nil;
        if ([childContext save:&childError]) {
            [parentContext performBlock:^{
                NSError *parentError = nil;
                if (![parentContext save:&parentError]) {
                    NSLog(@"Error saving parent");
                }
            }];
        } else {
            NSLog(@"Error saving child");
        }
    }];
}

ここで、子コンテキストで行われた変更 (挿入されたエンティティなど) は、保存するまで親コンテキストで使用できないことに注意する必要があります。子コンテキストに対して、親コンテキストは永続ストアです。保存すると、それらの変更が親に渡され、親はそれらを実際の永続ストアに保存できます。伝播の変更を 1 レベル上に保存します。一方、子コンテキストへのフェッチは、すべてのレベルを介して (親を介して子に) データを取得します。

  1. objectWithIDmanagedObjectContextで何らかの形式を使用する必要があります。これらは、コンテキスト間でオブジェクトを渡す最も安全な (そして実際に唯一の) 方法です。Tom Harrington がコメントで述べたように、無効な ID (例外につながる可能性がある) を渡した場合でも、常にオブジェクトを返すexistingObjectWithID:error:ため、使用することをお勧めします。objectWithID:詳しくはこちら:リンク
于 2013-01-11T20:19:58.780 に答える
6

同様の問題がありましたが、質問の一部に対する回答をNSPrivateQueueConcurrencyType以下に示しNSMainQueueConcurrencyType ます. その場合、管理対象オブジェクトを次の場所に移動するだけです-tempContextmainContexttempContextmainContext

object = (Object *)[mainContext objectWithID:object.objectID];

その後、mainContext 自体を保存できます。

多分また、

[childContext reset];

一時的なコンテキストをリセットしたい場合。

于 2013-01-11T19:46:38.290 に答える
6
  1. 親/子パターンを使用する場合、通常、親コンテキストを で宣言しNSMainQueueConcurrencyType、子コンテキストを で宣言しNSPrivateQueueConcurrencyTypeます。NSConfinementConcurrencyType古典的な糸通しパターンに使用されます。

  2. コンテキストを保持したい場合は、どういうわけかそれへの強力な参照が必要です。

  3. 変更を親コンテキストにプッシュするには、子コンテキストで save メソッドを呼び出すだけです。データを永続化する場合は、親コンテキストでも save メソッドを呼び出します。ブロック内でこれを行う必要はありません。

  4. コンテキストから特定のオブジェクトを取得する方法はいくつかあります。あなたのケースでどれがうまくいくかはわかりません。試してみてください:

    - objectRegisteredForID:

    - objectWithID:

    - existingObjectWithID:error:

于 2013-01-11T19:40:31.067 に答える