KVO: Observer un NSArray sous conditions
laurris
Membre
Voilà la situation:
J'ai une collection d'objets mise sous observation KVO avec addObserver:...
J'utilise le handler -(void)observeValueForKeyPath ... pour appeler une méthode X après une Insertion ou après une suppression d'objet (selon les valeurs du change dictionary).
Voilà ce que je voudrais faire:
J'ai besoin de faire une suppression, puis une insertion et seulement APRES appeller la méthode X. En l'état, cette méthode serait appelée 2 fois.
Je pourrais utiliser une ivar, modifiée avant chaque changement dans la collection, qui dirait appellle_la_methode_x = YES / NO. Mais ça se révèle trop compliqué et pas assez fiable.
Est-ce que vous savez quelle est la bonne approche ?
J'ai pensé utiliser le Context dans addObserver:self
forKeyPath:key
options:nil
context:Context
Mais dans ce cas, pour permettre les différents cas possibles, il faudrait que la collection soit observée par deux observers en même temps (avec des context différents). Est-ce autorisé par Mr Cocoa ?
Mes hommages,
Laurris.
J'ai une collection d'objets mise sous observation KVO avec addObserver:...
J'utilise le handler -(void)observeValueForKeyPath ... pour appeler une méthode X après une Insertion ou après une suppression d'objet (selon les valeurs du change dictionary).
Voilà ce que je voudrais faire:
J'ai besoin de faire une suppression, puis une insertion et seulement APRES appeller la méthode X. En l'état, cette méthode serait appelée 2 fois.
Je pourrais utiliser une ivar, modifiée avant chaque changement dans la collection, qui dirait appellle_la_methode_x = YES / NO. Mais ça se révèle trop compliqué et pas assez fiable.
Est-ce que vous savez quelle est la bonne approche ?
J'ai pensé utiliser le Context dans addObserver:self
forKeyPath:key
options:nil
context:Context
Mais dans ce cas, pour permettre les différents cas possibles, il faudrait que la collection soit observée par deux observers en même temps (avec des context différents). Est-ce autorisé par Mr Cocoa ?
Mes hommages,
Laurris.
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Un appel explicite à will/didChange plutôt qu'une surveillance automatique ?
Ou alors dans le cas de ta suppression puis insertion, ne pas appeler les setter/getters ([... setValue:...] etc) mais directement les valeurs de ta classe sans setter/getter (pour que la notification willChange/didCHange ne soit pas rajouté dans le code et appelé automatiquement) ?
Ce ne sont que des idées en vrac, mais bon comme si tu n'appelles pas les méthodes get/set "setVariable" et "variable" mais modifies la _variable directement ça n'ajoutera pas willChangeValueForKey et didChangeValueForKey tout seul, ça peut être à exploiter.
Ca permet de réarranger son array à loisir dans son coin, puis de mettre en route l'observation avec en argument un NSKeyValueChangeKind particulier. C'est ce dernier point qui m'est utile dans mon cas puisque je ne veux pas que l'obervateur interprète mon changement comme une insertion ou une suppression. J'ai pas encore essayé mais c'est sans doute ce qui convient.
Bon, en réalité un autre problème est apparu entre-temps qui m'empêche de tester cette solution (comme dab). J'explique: mon réarrangement consiste en un changement d'index de mes objet à l'intérieur de l'array. On dirait qu'il n'existe pas de méthode NSMutableArray toute faite pour ça, ce qui est surprenant. Je cheche quelque chose du genre:
Je suis sûr (ne niez pas !) que vous avez déjà fait un truc comme ça ... et je sens que je vais m'arracher les cheveux avec les replacements d'index. Alors, à vot' bon coeur sieu' dames ...
Ou est-ce que tu en fais en série ? J'imagine que ce n'est pas juste pour trier ton tableau, tu aurais utilisé les méthodes "sortXXX" avec le critère de tri de ton choix, aussi alambiqué soit-il.
Je n'ai pas de méthode tout faite, mais comme ça au feeling, il faut au moins déjà penser à l'ordre des opérations :
Bref, il faut bien définir ce que tu entends par "mettre l'élément qui est à la position X en position Y", les positions, et en particulier Y, pour toi c'est l'index "actuel", ou après la suppression de l'élément de sa position X et avant la réinsertion ?
A mon avis d'ailleurs c'est aussi pour tout ce genre d'ambigà¼ités dans la définition qu'il n'y a pas de méthodes prédéfinies :
- Il y a de quoi enlever un élément à une position X, aussi de quoi en insérer un à une position Y, et même de quoi échanger les éléments aux positions X et Y entre eux... mais replacer un élément X à la position Y, la définition en particulier dudit Y n'est pas suffisament claire.
- Alors que si tu le fais manuellement 2 temps (enlèvement de la valeur puis insertion... ou dans l'autre sens), tu sais explicitement dans quel sens tu fais les opérations.
---
Une solution la plus propre étant à mon avis de récupérer (sans l'extraire) l'élément à la position X ([tt]id obj = [tableau valueAtIndex:X];[/tt]), pour pouvoir l'insérer en position Y ([tt][tableau insertObject:obj atIndex:Y];[/tt]), et enfin le supprimer de sa position initial, mais en prennant soin de vérifier le décalage d'indice ([tt][tableau removeObjectAtIndex: (X<Y)?X:X+1];[/tt])
Ca ne résoud pas ton problème de double-notification, mais déjà sans ce souci tu vois qu'il faut penser à certains détails
Merci pour ta réponse très détaillée. Il s'agit d'un réarrangement manuel à l'intérieur d'une collection. Comme pour le dra'n drop de lignes à l'intérieur d'un tableau.
Donc en réalité, je ne sais pas à l'avance si l'index source < index destination. Il faut que je prenne en compte les deux cas.
Je vais essayer ta méthode insertion+suppression. Juste un truc qui m'étonne: si j'insère un objet de l'array dans l'array, il ne faut pas faire une copie avant ? Sinon on aurait le même objet à deux endroits différents , ce qui pose de graves problèmes déontologiques, je crois.
Edit: ça a l'air de marcher impec. Voici le code si ça peut être utile:
(Le drop d'une sélection multiple la prochaine fois, à chaque jour suffit sa peine)
Quant à savoir pourquoi l'auto-insertion d'un objet de l'array a l'air de marcher, je suppose que c'est parce que l'array fait lui-même une copie des objets avant de les insérer. Mais si quelqu'un peut confirmer celà , je n'en serai que plus rassuré.
1) Merci d'éviter de faire une citation de tout le message précédent pour rien, ça surcharge le forum et n'a aucune utilité ! Le but des citations est plutôt de citer une partie précise d'un message poru répondre à une question précise, pas de dupliquer la quantité de texte sur les forums !
2) Sinon je ne vois pas ce qui te gène dans l'idée d'avoir 2 fois le même objet dans ton tableau ? Ce ne sont que des références.
le NSArray "retain" les éléments qu'il contient, tout comme tous les containers de Cocoa.
Un code d'exemple vaut mieux qu'un long discours : Tu remarqueras que quand je change la chaà®ne de monObj, les 2 chaines dans le tableau changent aussi (puisque c'est 3x le même objet, et pas 3 copies différentes), ce qui montre que ce ne sont que des références qui sont ajoutées dans le tableau, pas des copies. C'est le mécanisme de retain/release (et donc le retainCount) qui gèrent le tout.
Comme d'hab, quoi, tout est dans la doc :
Conclusion : il n'y a jamais eu de limitation interdisant de mettre plusieurs fois le même objet dans un NSArray, tout comme il n'a jamais été interdit d'avoir plusieurs variables pointant vers / utilisant le même objet !
Du coup, fais attention aux effets de bord si t'as le même objet "mutable" plusieurs fois dans un tableau et que t'en modifies un
Cette méthode permet de réarranger manuellement plusieurs objets à l'intérieur d'une collection et de façon conforme au KVO. Si l'objet est observé, il se signale par un type de changement NSKeyValueReordering qui est une constante prédéfinie.
Remplacer <key> par le chemin de l'objet par rapport à SELF.