La détection des changements à un attribut spécifique de NSManagedObject
Comment puis-je détecter les modifications apportées à un attribut spécifique d'un NSManagedObject
? Dans ma Base de Données modèle de données, j'ai un Product
entité qui représente un produit pour la vente. Le Product
entité possède plusieurs attributs: price
, sku
, weight
, numberInStock
, etc. Chaque fois que le price
attribut d'une Product
changements, j'ai besoin d'effectuer un long calcul. Par conséquent, je voudrais savoir quand le price
attribut de tout Product
changements, [edit] même si ce changement provient de la fusion d'un contexte sauvegardé sur un autre thread. Ce qui est une bonne façon de faire? J'ai des milliers de Product
objets dans ma boutique; évidemment, il n'est pas possible d'envoyer chacun un addObserver
message.
J'ai été en utilisant NSManagedObjectContextObjectsDidChangeNotification
pour détecter les changements, mais il ne m'avise que un objet géré a changé, pas qui attribut de l'objet a changé. J'ai pu refaire le calcul à chaque fois qu'il y a tout changer pour un Product
, mais que les résultats inutiles recalculs chaque fois qu'un attribut de pertinence a changé. J'envisage de faire un Price
entité (qui ne contient qu'un price
attribut) et à l'aide d'une relation entre Product
et Price
. De cette façon, je peux détecter les changements de Price
objets afin de lancer le calcul. Cela semble trop encombrants pour moi. Est-il un meilleur moyen?
Mise à jour:
@railwayparade remarquer que je pouvais utiliser le changedValues
méthode de NSManagedObject
pour déterminer les propriétés ont été modifiées pour chaque objet mis à jour. J'ai complètement raté cette méthode, et il serait totalement résoudre mon problème, si les modifications n'ont pas été faites sur un thread d'arrière-plan et fusionnés dans le contexte du thread. (Voir le paragraphe suivant.)
J'ai complètement loupé une subtilité dans la manière dont NSManagedObjectContextObjectsDidChangeNotification
œuvres. Aussi loin que je peux dire, quand un objet géré contexte sauvegardé sur un autre thread est fusionné dans un contexte du thread principal (à l'aide d'un mergeChangesFromContextDidSaveNotification:
), le résultat NSManagedObjectContextObjectsDidChangeNotification
seulement contient modifier des informations sur des objets qui sont actuellement dans le thread principal est géré contexte de l'objet. Si un changement de l'objet n'est pas dans le thread principal du contexte, il ne sera pas une partie de la notification. Il est logique, mais n'est-ce pas ce à quoi je m'attendais. Donc, mon idée d'utiliser une relation au lieu d'un attribut dans l'ordre pour obtenir plus d'informations de changement en fait, il faut examiner le contexte du thread NSManagedObjectContextDidSaveNotification
, pas le thread principal de l' NSManagedObjectContextObjectsDidChangeNotification
. Bien sûr, il serait beaucoup plus intelligent d'utiliser simplement la changedValues
méthode de NSManagedObject
comme @railwayparade obligeamment indiqué. Cependant, je suis toujours à gauche avec le problème que la notification de modification de la fusion sur le thread principal ne sera pas nécessairement contenir toutes les modifications effectuées sur le thread d'arrière-plan.
Vous devez vous connecter pour publier un commentaire.
Ce type de circonstance où vous avez besoin d'un custom NSManagedObject sous-classe. Vous avez besoin de la sous-classe en raison de l'ajout d'un comportement, de réagir à un changement de prix, à l'objet géré.
Dans ce cas, vous devez remplacer l'accesseur pour le
price
attribut. Créer une sous-classe personnalisée en utilisant le menu contextuel de l'éditeur de modèle de données. Ensuite, sélectionnez leprice
attribut et sélectionner " Copier l'Obj-C 2.0 mise en Œuvre dans le presse-papiers`. Il va vous donner beaucoup de choses mais le plus important ressemblera à ceci:Il suffit d'ajouter le code à composer avec le changement de prix et vous êtes fait. Chaque fois qu'un produit en particulier des changements de prix, le code sera exécuté.
Un point à l'égard de ce fil,
La réalité. Le "changedValues" la méthode peut être utilisée pour interroger les attributs changé.
Quelque chose comme,
NSManagedObjectContextObjectsDidChangeNotification
. Et puis, pour savoir ce qui a changé pour chaque mise à jourNSManagedObject
, j'ai simplement regarder à travers l'objet enchangedValues
dictionnaire. Génial! Ça ne résout pas le cross-thread fusion problème, lors de la fusion d'un contexte sauvegardé sur un thread d'arrière-plan,NSManagedObjectContextObjectsDidChangeNotification
inclut uniquement les modifications apportées aux objets actuellement dans le thread principal du contexte--mais il est certainement un super-méthode utile que je peux utiliser dans beaucoup d'autres endroits. Merci, @railwayparade!changedValuesForCurrentEvent
tropVous pouvez prendre un coup d'oeil à KVO (Valeur de la Clé de l'Observation). Vous ne savez pas si il y a des gestionnaires intégrés dans la Base de Données de l'API, mais je sais que c'est une partie de l'Objective-C.
addObserver
message à chaqueProduct
objet que je désire observer. Il y a des milliers deProduct
objets dans ma boutique. Chargement de tout le monde en à lancer juste pour les observer paraît excessif. L'ajout d'un observateur dansawakeFromFetch
semble mieux, mais parfois mon contexte des changements viennent de la fusion contexte enregistre faite par un autre thread. Je ne suis pas sûr de comment cela pourrait fonctionner.Je pensais document mes décisions de conception ici dans le cas où ils sont utiles à d'autres. Ma dernière solution était basée sur TechZen de réponse.
Tout d'abord, je vais commencer par un bref, et espérons-le, plus claire, reformulation du problème:
Dans ma demande, je veux détecter les changements à un attribut spécifique (
price
) d'un objet managé (Product
). En outre, je veux savoir à propos de ces modifications, si elles sont faites sur-le-main ou un thread d'arrière-plan. Enfin, je veux savoir à propos de ces changements, même si le thread principal n'a pas la changéProduct
objet d'un programme de gestion du contexte de l'objet.La
NSManagedObjectContextObjectsDidChangeNotification
généré par la Base de Données indique que un objet géré a changé, mais n'indique pas qui attribut a changé. Mon encombrants solution était de créer unePrice
géré objet contenant un seulprice
attribut, et de remplacer leprice
attribut dansProduct
avec une relation à unPrice
objet géré. Maintenant, chaque fois qu'une modification est apportée à unPrice
géré objet, la Base de DonnéesNSManagedObjectContextObjectsDidChangeNotification
contiendra quePrice
objet dans sonNSUpdatedObjectsKey
ensemble. J'ai tout simplement besoin d'obtenir cette information pour le thread principal. Tout cela sonne bien, mais il y a un hic.De mon coeur de magasin de Données est d'être manipulés par deux threads. Ceci est fait dans le cadre "habituel" façon—il est géré contexte de l'objet pour chaque thread et une seule partagé persistante coordonnateur du magasin. Après le thread d'arrière-plan apporte des modifications, il enregistre son contexte. Le thread principal détecte le contexte enregistrer via le
NSManagedObjectContextDidSaveNotification
et fusionne les changements de contexte à l'aide demergeChangesFromContextDidSaveNotification:
. (En fait, depuis les notifications sont reçues dans le même thread, ils sont affichés dans leNSManagedObjectContextDidSaveNotification
est reçu sur le thread d'arrière-plan et est passé pour le thread principal viaperformSelectorOnMainThread:
pour la fusion.) Comme un résultat de la fusion, de Base de Données génère unNSManagedObjectContextObjectsDidChangeNotification
indiquant les objets modifiés. Cependant, autant que je peux dire, leNSManagedObjectContextObjectsDidChangeNotification
inclut uniquement les objets qui sont actuellement représentées dans le contexte. Cela fait sens du point de vue de la mise à jour de l'INTERFACE utilisateur. Si un objet géré n'est pas affiché, il ne sera probablement pas dans le contexte, et donc il n'y a pas besoin de l'inclure dans la notification.Dans mon cas, mon thread principal besoin de savoir sur les modifications apportées aux objets gérés si oui ou non ils sont actuellement dans le thread principal du contexte. Si tout changements de prix, le thread principal besoins de la file d'attente d'une opération de processus de changement de prix. Par conséquent, le thread principal doit savoir au sujet de tous changements de prix, même si ces modifications sont effectuées sur un thread d'arrière-plan pour un produit qui n'est pas actuellement accessible sur le thread principal. Évidemment, depuis
NSManagedObjectContextObjectsDidChangeNotification
ne contient que des informations sur les objets actuellement dans le thread principal du contexte, il ne répond pas à mes besoins.La deuxième option, j'ai pensé à utiliser le
NSManagedObjectContextDidSaveNotification
généré par le thread d'arrière-plan lorsqu'il enregistre son contexte. Cette notification contient des informations sur tous changements pour les objets gérés. J'ai déjà de détecter cette notification et la passer au thread principal pour la fusion, alors pourquoi ne pas le coup d'oeil l'intérieur et de voir tous les objets gérés qui ont changé? Vous aurez rappeler que les objets gérés sont pas destinés à être partagés sur les threads. Par conséquent, si je commence à examiner le contenu d'NSManagedObjectContextDidSaveNotification
sur le thread principal, je reçois des accidents. Hmm ... alors comment nemergeChangesFromContextDidSaveNotification:
le faire? Apparemment,mergeChangesFromContextDidSaveNotification:
est spécifiquement conçu pour fonctionner autour de l' "ne pas partager les objets gérés dans les threads" restriction.La troisième option, j'ai pensé à enregistrer pour
NSManagedObjectContextDidSaveNotification
sur le thread d'arrière-plan et tout sur le thread d'arrière-plan convertir son contenu dans un spécialPriceChangeNotification
contenant les Id d'objet au lieu d'objets gérés. Sur le thread principal, j'ai pu convertir les Id d'objet en arrière dans les objets gérés. Cette approche nécessiterait encore la pour un,Price
relation de sorte que les changements de prix sont comptabilisés comme des changements dePrice
objets gérés.J'ai basé mon quatrième option sur TechZen la suggestion de remplacer le prix setter dans le
Product
objet géré. Plutôt que d'utiliser une relation de force de Base de Données pour générer des notifications dont j'avais besoin, je suis retourné à l'aide d'unprice
attribut. Dans monsetPrice
méthode, je poste une coutumePriceChangeNotification
. Cette notification est reçue sur le thread d'arrière-plan et est utilisé pour construire un ensemble deProduct
objets avec des changements de prix. Après le thread d'arrière-plan d'économiser son contexte, elle affiche une coutumePricesDidChangeNotification
qui inclut l'Id d'objet de tous lesProduct
objets dont les prix ont changé. Cette notification peut être transféré en toute sécurité pour le thread principal et examiné, car il utilise les Id d'objet à la place de la gestion des objets eux-mêmes. Sur le thread principal, je peux aller chercher leProduct
objets référencés par ces Identifiants d'objets de la file d'attente d'une opération à effectuer la longue "changement de prix" calcul sur un nouveau thread d'arrière-plan.Êtes-vous à l'aide d'un
NSArrayController
ou une autre contrôleur? Je suppose que vous devez trouver un moyen pour l'utilisateur d'interagir avec le modèle. C'est ce point d'interaction qui donne un bon crochet pour ce type de mise à jour de l'appel. Peut-être la meilleure stratégie est de respecter les propriétés de la matrice du contrôleur dearrangedObjects
.arrangedObjects
aurais-je observer? Ou voulez-vous dire ajouter un observateur de laprice
attribuer à chaque objet dansarrangedObjects
? Je ne suis pas sûr que cela fonctionne. À tout moment, le thread principal uniquement a des références à un sous-ensemble de produits. Un thread d'arrière-plan est la mise à jour de produits, y compris leur prix, de sorte que, parfois, le thread principal est seulement conscience qu'un produit a changé viaNSManagedObjectContextObjectsDidChangeNotification
à partir d'une fusion. Il juste ne peut pas dire si c'était le prix ou un autre attribut qui a changé. Donc, elle ne sait pas si pour recalculer ou pas.