web-dev-qa-db-ja.com

CoreDataは2つの管理対象オブジェクトコンテキストをマージします

私のCocoa/Applicationには、メインスレッドに管理対象オブジェクトコンテキストがあります。データを更新する必要がある場合、プログラムは次のようになります。

  1. 新しいスレッドを開始します
  2. サーバーから新しいデータを受信する
  3. 新しい管理対象オブジェクトコンテキストを作成する
  4. 2つのコンテキストをマージするために、メインスレッドに通知を送信します

メインスレッドで通知を受け取る関数です

- (void)loadManagedObjectFromNotification:(NSNotification *)saveNotification
{
    if ([NSThread isMainThread]) {
        [self.managedObjectContext mergeChangesFromContextDidSaveNotification:saveNotification];
    } else {
        [self performSelectorOnMainThread:@selector(loadManagedObjectFromNotification:) withObject:saveNotification waitUntilDone:YES];     
    }
}

エラーは発生しません。私の問題はマージ結果です。実際には、両方のコンテキストから管理対象オブジェクトを連結します。

私のエンティティは、属性と関係の非常に単純なリストです。

更新された管理対象オブジェクトIS新しいものではなく、最初のオブジェクトの編集バージョン)を理解するために、マージにはいくつかの指示が必要な場合があります。どこかで方法を指定する必要があると思います。エンティティ(たとえば、属性はIDのように機能できます)とマージポリシーのようなもの(2つの管理対象オブジェクトが同じオブジェクトを表す場合は、lastModificationDateの新しいオブジェクトを取得します)を一義的に識別します。

オブジェクトごとに1つの更新されたコピーを作成するには、2つのコンテキストを正しくマージする方法を理解する必要があります。

更新1

問題は今や私には明らかです。 2つのコンテキストには大きな違いがあります:ObjectID。メインスレッドのコンテキストが永続ストアコーディネーターを使用してManagedObjectsをフェッチしている間、2番目のスレッドはリモートURLをフェッチしてそれらのオブジェクトを作成します。オブジェクトの内容が同じであっても、2つの異なるobjectIDがあります。

私のオブジェクトにはすでに一意の識別子があり、この値を設定するためにsetObjectIdを使用できました。 (Appleのドキュメントによると、これは良い考えではありません)。

22
Giuseppe

コンテキストを正しくマージするために必要なことは次のとおりです。まず、独自の通知は必要ありません。コンテキストで保存操作を実行すると、次の通知が登録済みのオブザーバーに自動的に転送されます。

NSManagedObjectContextDidSaveNotification

したがって、あなたがする必要があるのは:

1)メインスレッドで、viewDidLoadメソッドにある可能性があります。この通知に登録してください:

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(contextDidSave:)
                                             name:NSManagedObjectContextDidSaveNotification
                                            object:nil];

2)次のようにメインスレッドにcontextDidSave:メソッドを実装します。

- (void)contextDidSave:(NSNotification *)notification
{

    SEL selector = @selector(mergeChangesFromContextDidSaveNotification:); 
    [managedObjectContext performSelectorOnMainThread:selector withObject:notification waitUntilDone:YES];

}

3)deallocメソッドに以下を追加します。

[[NSNotificationCenter defaultCenter] removeObserver:self];

4)次の方法のようなものを使用して、他のスレッドに新しいコンテキストを作成します。

- (NSManagedObjectContext*)createNewManagedObjectContext
{

    NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init]; 
    [moc setPersistentStoreCoordinator:[self persistentStoreCoordinator]];
    [moc setUndoManager:nil];
    return [moc autorelease];
}

5)新しいデータを受信したら、この状況を処理する適切な方法は、管理対象オブジェクトIDを使用することです。管理対象オブジェクトIDはスレッドセーフであるため、メインスレッドから他のスレッドにそれらを渡し、existingObjectWithID:error:を使用して特定のIDに関連付けられたオブジェクトを取得し、更新してコンテキストを保存できます。これで、マージは期待どおりに機能します。または、スレッド間で渡す必要のある管理対象オブジェクトIDが事前にわからない場合は、他のスレッドで、述語を使用してオブジェクトをフェッチし、サーバーから取得したオブジェクトに対応するオブジェクトを取得してから、それらを更新します。コンテキストを保存します。

33
Massimo Cafaro