22

私のCocoa/Applicationは、メインスレッド上にマネージオブジェクトコンテキストを持っています。私は私のデータを更新する必要がある場合 私のプログラムがされます:コアデータを2つのマネージオブジェクトコンテキストをマージする

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

これは、メインスレッドの通知を受信する機能である

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

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

マイエンティティは、属性と関係の本当に単純なリストです。

おそらく、更新された管理オブジェクトが新しいものではなく、最初のものの編集されたバージョンであることを理解するために、マージがいくつかの指示を必要とするかもしれません。 私は、Entity(IDのように動作する属性)とマージポリシーのようなものを一意に識別する方法を指定する必要があると思います(2つの管理対象オブジェクトが同じオブジェクトを表す場合は、lastModificationDateより最近)。

オブジェクトごとに1つの更新されたコピーを持つために2つのコンテキストを正しくマージする方法を理解するだけでいいです。

更新1

この問題は現在明らかです。 2つのコンテキストに大きな違いがあります:ObjectID。 メインスレッドのコンテキストがPersistent Storeコーディネータを使用してManagedObjectをフェッチしている間、2番目のスレッドはリモートURLをフェッチすることによってそれらのオブジェクトを作成します。たとえオブジェクトの内容が同じであっても、2つの異なるobjectIDを持ちます。

私のオブジェクトはすでに一意の識別子を持っていましたので、この値を設定するためにsetObjectIdを使用できます。 (Appleの文書によれば、これは良い考えではない)

+1

オブジェクトIDが異なることは明らかです。なぜなら、ストアからオブジェクトをフェッチして更新するのではなく、オブジェクトを作成するからです。それで、私はあなたがオブジェクトを結合するために他の何かを必要としなかったという私の答えで言ったのです。とにかく、この状況を処理する適切な方法は、管理対象オブジェクトIDの使用です。管理オブジェクトIDはスレッドセーフなので、メインスレッドから別のスレッドに渡すことができます。次に、existingObjectWithID:error:を使用して、特定のIDに関連付けられたオブジェクトを取得し、更新してコンテキストを保存します。これで、マージが期待どおりに動作します。 –

+0

また、スレッド間でどの管理対象オブジェクトIDを渡す必要があるかを事前に把握していない場合は、別のスレッドで単に述部を使用してオブジェクトをフェッチし、サーバーから取得したオブジェクトに対応するオブジェクトを取得し、コンテキストを保存します。 –

+0

正解!問題はobjectIdsでした。フェッチを行い、既存のオブジェクトを更新する必要があるたびに、ストアからオブジェクトを取得して更新します。 – Giuseppe

答えて

32

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

NSManagedObjectContextDidSaveNotification 

をそのため、すべてを行う必要がある:

あなたのメインスレッドで

1)、viewDidLoad方法であってもよく、この通知を登録:

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

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

あなたの dealloc方法で

3)は、以下を追加します。

[[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を渡す必要があるかを事前に把握していない場合、別のスレッドでは、述部を使用してオブジェクトをフェッチして、サーバーから取得したオブジェクトに対応するオブジェクトを取得し、コンテキストを保存します。

+0

これは私の実装と全く同じです。 私の問題はマージです。私が電話をかけたとき [self.managedObjectContext mergeChangesFromContextDidSaveNotification:saveNotification]; ManagedObjectをマージする必要がありますが、連結するだけです。 – Giuseppe

+1

関連するソースコードのすべてを見ることなく、実装に何が問題なのかを知ることは難しいです。他のスレッドのコンテキストにデータを保存してもよろしいですか?保存が必要です(マージ操作でメモリ内オブジェクトとストアオブジェクトを比較するため)が、あなたの質問には言及していません。マージポリシーについてデフォルトのものがあなたのニーズに合わない場合は、確かに各コンテキストに1つずつ設定することができます。繰り返しますが、詳細な説明がなくても、それを伝えるのは難しいです。 –

+0

この回答は私がネット上で見つけた唯一の正しい説明です。 –

関連する問題