23

の特定の属性に対する変更を検出するにはどうすればよいNSManagedObjectですか? Core Data データ モデルには、Product販売する製品を表すエンティティがあります。ProductエンティティにはpriceskuweightnumberInStock、 などのいくつかの属性があります。 の属性が変更されるたびに、price長いProduct計算を実行する必要があります。したがって、その変更が別のスレッドに保存されたコンテキストのマージに起因する場合でも、[編集]priceの属性がいつ Product変更されるかを知りたいです。これを行うための良い方法は何ですか? 私のストアには何千ものオブジェクトがあります。明らかに、それぞれにメッセージを送ることは現実的ではありません。ProductaddObserver

変更を検出するために使用していますが、管理対象オブジェクトが変更されたことをNSManagedObjectContextObjectsDidChangeNotification通知するだけで、そのオブジェクトのどの属性が変更されたかは通知されません。に変更があるたびに計算をやり直すことができますが、無関係な属性が変更されるたびに無駄な再計算が行われます。エンティティ(属性のみを含む)を作成し、との間の対1関係を使用することを検討しています。このようにして、計算を開始するためにオブジェクトの変更を検出できます。これは私には過度に不器用に思えます。より良い方法はありますか?ProductPricepriceProductPricePrice

アップデート:

changedValues@railwayparade は、 のメソッドを使用しNSManagedObjectて、更新されたオブジェクトごとに変更されたプロパティを特定できることを指摘しました。私はその方法を完全に見逃していました。変更がバックグラウンド スレッドで行われておらず、メイン スレッドのコンテキストにマージされていなければ、問題は完全に解決されます。(次の段落を参照してください。)

NSManagedObjectContextObjectsDidChangeNotification私はその仕組みについての微妙な点を完全に見逃していました。私が知る限り、別のスレッドに保存されたマネージド オブジェクト コンテキストが (を使用してmergeChangesFromContextDidSaveNotification:) メイン スレッドのコンテキストにマージされると、結果には現在メイン スレッドのマネージド オブジェクト コンテキストにあるオブジェクトに関する変更情報NSManagedObjectContextObjectsDidChangeNotification のみが含まれます。変更されたオブジェクトがメイン スレッドのコンテキストにない場合、それは通知の一部にはなりません。それは理にかなっていますが、私が予想していたものではありませんでした。したがって、より詳細な変更情報を取得するために属性の代わりに対 1 関係を使用するという私の考えでは、実際にはNSManagedObjectContextDidSaveNotificationメイン スレッドではなく、バックグラウンド スレッドの を調べる必要がありますNSManagedObjectContextObjectsDidChangeNotification。もちろん、単に次のchangedValues方法を使用する方がはるかに賢明です。NSManagedObject@railwayparade が有益に指摘したように。ただし、メイン スレッドでのマージからの変更通知に、バックグラウンド スレッドで行われたすべての変更が含まれているとは限らないという問題がまだ残っています。

4

5 に答える 5

43

このスレに関して、一点、

Core Data によって生成された NSManagedObjectContextObjectsDidChangeNotification は、管理対象オブジェクトが変更されたことを示しますが、どの属性が変更されたかは示しません。

実際にそうです。「changedValues」メソッドを使用して、どの属性が変更されたかを照会できます。

何かのようなもの、

 if([updatedObjects containsKindOfClass:[Config class]]){
    //if the config.timeInterval changed
    NSManagedObject *obj = [updatedObjects anyObject];
    NSDictionary *dict=[obj changedValues];
    NSLog(@"%@",dict);
    if([dict objectForKey:@"timeInterval"]!=nil){
      [self renderTimers];
    }
  }
于 2011-06-23T02:14:36.713 に答える
10

このような状況では、カスタム NSManagedObject サブクラスが必要になります。価格の変化に反応する動作をマネージド オブジェクトに追加するため、サブクラスが必要です。

この場合、price属性のアクセサーをオーバーライドします。データ モデル エディタのポップアップ メニューを使用して、カスタム サブクラスを作成します。次に、price属性を選択し、[Obj-C 2.0 実装をクリップボードにコピー] を選択します。それはあなたに多くのものを与えますが、重要なビットは次のようになります:

- (void)setPrice:(NSNumber *)value 
{
    [self willChangeValueForKey:@"price"];
    [self setPrimitivePrice:value];
    [self didChangeValueForKey:@"price"];
}

価格変更を処理するコードを追加するだけで完了です。特定の製品の価格が変更されるたびに、コードが実行されます。

于 2010-10-12T21:47:51.227 に答える
3

KVO (Key Value Observing) を見てみましょう。Core Data API に組み込まれたラッパーがあるかどうかはわかりませんが、Objective-C の一部であることはわかっています。

于 2010-10-10T16:55:13.847 に答える
1

他の人に役立つ場合に備えて、ここで私の設計上の決定を文書化すると思いました。私の最終的な解決策は、TechZen の回答に基づいていました。

最初に、問題を簡潔に、できればより明確に説明し直すことから始めます。

priceアプリケーションで、管理対象オブジェクト ( ) の特定の属性 ( ) に対する変更を検出したいと考えていますProduct。さらに、それらの変更がメイン スレッドで行われたのかバックグラウンド スレッドで行われたのかを知りたいです。Product最後に、現在メイン スレッドの管理対象オブジェクト コンテキストに変更されたオブジェクトがない場合でも、これらの変更について知りたいと思います。

NSManagedObjectContextObjectsDidChangeNotificationCore Data によって生成されたは、管理対象オブジェクトが変更されたことを示しますが、どの属性が変更されたかは示しません。私の無謀な解決策はPrice、単一の属性を含む管理対象オブジェクトを作成し、その属性を管理対象オブジェクトとの 1 対 1 の関係にprice置き換えることでした。これで、管理対象オブジェクトに変更が加えられるたびに、Core Data のセットにそのオブジェクトが含まれます。この情報をメインスレッドに渡すだけです。これはすべて良さそうですが、問題があります。priceProductPricePriceNSManagedObjectContextObjectsDidChangeNotificationPriceNSUpdatedObjectsKey

Core Data ストアが 2 つのスレッドによって操作されています。これは「通常の」方法で行われます。つまり、スレッドごとに管理対象オブジェクト コンテキストがあり、単一の共有永続ストア コーディネーターがあります。バックグラウンド スレッドが変更を行った後、そのコンテキストを保存します。メイン スレッドは、 を介してコンテキストの保存を検出し、NSManagedObjectContextDidSaveNotificationを使用してコンテキストの変更をマージしますmergeChangesFromContextDidSaveNotification:。(実際には、通知はポストされたのと同じスレッドでNSManagedObjectContextDidSaveNotification受信されるため、バックグラウンド スレッドで受信され、マージのためにメイン スレッドに渡されますperformSelectorOnMainThread:。) マージの結果、Core Data はNSManagedObjectContextObjectsDidChangeNotification、変更されたオブジェクトを示す を生成します。 . ただし、私が知る限り、NSManagedObjectContextObjectsDidChangeNotification受信コンテキストで現在表されているオブジェクトのみが含まれます。これは、UI を更新するという観点からは理にかなっています。管理オブジェクトが表示されていない場合は、おそらくコンテキストにないため、通知に含める必要はありません。

私の場合、メイン スレッドは、現在メイン スレッドのコンテキストにあるかどうかに関係なく、マネージド オブジェクトに加えられた変更について知る必要があります。価格が変更された場合、メイン スレッドはその価格変更を処理する操作をキューに入れる必要があります。したがって、メイン スレッドで現在アクセスされていない製品のバックグラウンド スレッドで変更が行われたとしても、メイン スレッドはすべての価格変更を認識する必要があります。明らかに、NSManagedObjectContextObjectsDidChangeNotification現在メインスレッドのコンテキストにあるオブジェクトに関する情報しか含まれていないため、私のニーズを満たしていません。

私が考えた 2 番目のオプションはNSManagedObjectContextDidSaveNotification、コンテキストを保存するときにバックグラウンド スレッドによって生成された を使用することでした。この通知には、管理対象オブジェクトへのすべての変更に関する情報が含まれています。私はすでにこの通知を検出し、それをマージのためにメイン スレッドに渡しています。管理対象オブジェクトはスレッド間で共有されることを意図していないことを思い出してください。したがって、NSManagedObjectContextDidSaveNotificationメイン スレッドで の内容を調べ始めると、クラッシュが発生します。うーん...どうmergeChangesFromContextDidSaveNotification:やってそれを行うのですか?どうやら、mergeChangesFromContextDidSaveNotification:「スレッド間で管理対象オブジェクトを共有しない」という制限を回避するように特別に設計されています。

私が考えた 3 番目のオプションは、バックグラウンド スレッドに登録しNSManagedObjectContextDidSaveNotificationまだPriceChangeNotificationバックグラウンド スレッドにある間に、その内容を管理対象オブジェクトではなく特別なオブジェクト ID を含むものに変換することでした。メイン スレッドでは、オブジェクト ID を管理対象オブジェクトに戻すことができました。このアプローチでもPrice、価格の変更がPrice管理対象オブジェクトの変更として反映されるように、対 1 の関係が必要になります。

4 番目のオプションは、TechZen の提案に基づいて、Product管理対象オブジェクトの価格セッターをオーバーライドしました。priceCore Data に必要な通知を強制的に生成させるために 1 対 1 の関係を使用するのではなく、属性の使用に戻りました。私のsetPrice方法では、カスタムを投稿しますPriceChangeNotificationProductこの通知はバックグラウンド スレッドで受信され、価格変更を含む一連のオブジェクトを構築するために使用されます。バックグラウンド スレッドがそのコンテキストを保存した後、価格が変更されたPricesDidChangeNotificationすべてのオブジェクトのオブジェクト ID を含むカスタムを投稿します。Productこの通知は、管理対象オブジェクト自体ではなくオブジェクト ID を使用するため、安全にメイン スレッドに転送して調べることができます。メインスレッドでフェッチできますProductそれらのオブジェクト ID によって参照されるオブジェクトをキューに入れ、新しいバックグラウンド スレッドで時間のかかる「価格変更」計算を実行する操作をキューに入れます。

于 2010-10-16T20:33:27.657 に答える
0

NSArrayControllerまたは他のコントローラーを使用していますか?おそらく、ユーザーがモデルと対話するための何らかの方法が必要です。このタイプの更新呼び出しに適切なフックを提供するのは、この相互作用のポイントです。おそらく、適切な戦略は、配列コントローラーの関連するプロパティを観察することarrangedObjectsです。

于 2010-10-10T17:43:11.500 に答える