[Non conseillé] Retrouver le document courant à partir d'un NSManagedObject
berfis
Membre
Bonjour,
Je m'y perds un peu: sans utiliser de propriété supplémentaire à un NSManagedObject, ni faire appel au honni AppDelegate, est-il possible de retrouver la référence du document auquel l'objet appartient?
Merci.
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
On peut obtenir le NSManagedObjectContext dans lequel se trouve le NSManagedObject. À partir de là , on peut sans doute consulter la liste des NSPersistentDocuments pour savoir lequel utilise ce MOC. Au final, ce sera forcément compliqué et assez crade.
Je pense que le mieux est que tu nous exposes plutôt quel problème tu souhaites résoudre; j'ai l'impression que tu le prends à l'envers.
Pour le moment, je fais ça dans la méthode du NSManagedObject qui retourne la couleur du texte:
ça marche, mais est-ce que ce n'est pas trop "crade" ?
Nous manquons d'éléments pour comprendre ce que tu cherches à faire, mais il me semble que chaque ligne de la liste du bas correspond à une entité Test. Cette entité devrait sans doute posséder un attribut qui donne son statut ("Non répondu", "Correct", "Faux").
La difficulté est alors de changer la couleur du texte selon le statut. Il faut binder la couleur du texte de la ligne en fonction du statut, mais comment ? En créant une sous-classe de NSValueTransformer qui convertit le statut en NSColor.
Céroce,
J'avais utilisé un ValueTransformer pour une autre application, mais j'avais trouvé ça moins "propre" que de faire appel à une méthode de l'objet lui-même. Plutôt que de passer par une autre classe avec des méthodes peu évidentes à lui transmettre (avec forcément une méthode de classe genre +MyColorTransformer setMustDisplay: YES), je préfère demander à l'objet "De quelle couleur es-tu?". D'où une @property (readonly) NSColor *textColor.
Le problème est ainsi plus circonscrit, mais le problème demeure de savoir si une entité appartient au NSSet d'une autre entité particulière, cette particularité étant liée non au modèle lui-même, ce qui serait facile, mais à l'interface (la vue).
Une autre possibilité que j'avais envisagée est la suivante : le contrôleur du haut, lorsqu'il change la sélection, met à jour, pour chacune des entités qu'il contient, une variable d'instance (type transient) "isSelected". Dans ce cas, la seconde entité met sa couleur à jour en fonction de son appartenance (ou non) à un objet sélectionné.
Je ne sais pas, j'hésite. La solution ci-dessus est un "truc", mais il fonctionne. L'autre est peut-être plus OOP...
Pourquoi tu ne créerais une singleton (à la mode sharedInstance) "GestionnaireDeSélection", qui se souvient de qui sont tes éléments sélectionnés, et qui propose une méthode - isFollowingSentenceSelected: (Sentence *)aSentence ?
Pourquoi ne pas implémenter cette méthode directement dans ton ViewController ?
Ton ViewController pourrait implémenter une méthode qui donne la couleur d'une Sentence donnée.
Colas2,
La méthode est risquée. D'abord les sélections se font non seulement en fonction d'une sélection dans le contrôleur, mais également pour chaque document ouvert. Il s'agirait donc de tout sauf d'un "singleton".
L'autre risque, c'est les "retain cycles" avec cette manière de faire. Tout "conteneur", tout "contenu" peut être à tout moment supprimé par l'utilisateur. Cela multiplierait inutilement les pointeurs, nécessiterait la plus grande prudence pour éviter que les zombies surgissent de partout lors du désallouement. Je préfère laisser faire Core Data et ARC dans ce domaine...
Merci quand même!
L'intérêt des bindings c'est qu'un changement du modèle est répercuté automatiquement à l'affichage. Si ton code doit aller demander quelle est la couleur du Test, alors tu perds cet avantage.
Mais poursuivons, parce que changer la couleur par rapport au statut, n'est pas ce que tu sembles vouloir faire.
Je crois avoir enfin compris ce que tu essaies de faire: afficher en rouge les Sentences qui appartiennent aux Test sélectionnés.
En tout cas, le fait d'être sélectionné n'a pas apparaà®tre dans le modèle. C'est clairement de la responsabilité du Contrôleur.
À vrai dire, c'est complexe à faire. Voilà comment je m'y prendrais:
- il faut savoir quand la sélection du testsArrayController change. Pour cela, il y a plusieurs solutions:
1) créer une sous-classe de NSArrayController et surcharger -setSelectionIndexes:.
2) Faire du KVO sur la propriété selectionIndexes de testsArrayController.
3) Se mettre en délégué de la table view pour être averti des changements de sélection.
- on peut alors déterminer la liste des Sentences qui appartiennent aux Tests sélectionnés, en suivant les relations Core Data.
- forcer le rafraà®chissement de la table view.
- dessiner les lignes avec les bonnes couleurs.
1) Le plus simple est de ne pas utiliser les bindings: le data source regarde pour chaque ligne si la Sentence appartient aux Sentences sélectionnées et fixe la couleur selon.
2) avec les bindings, pas le choix, il faut utiliser un NSValueTransformer qui possède le NSSet des Sentences sélectionnées et qui détermine la couleur.
@Céroce : merci pour cette réponse très claire !
je n'avais jamais vu ça et je n'y avais pas pensé ! Malin !
Je crois me souvenir que selectionIndex s'observe mal
cf KVO selectionIndex bug (google)
C'est un bug assez connu (mais pas indiqué dans la doc d'Apple) qui n'a jamais été corrigé depuis 10.3. Effectivement le dico "change" est vide. Toutefois, on peut obtenir la nouvelle valeur par [object valueForKey:].
Oui, exact. Je te présente toutes mes confuses.
J'ai choisi ça. En fait, pour les deux tables.
Aaahh... mais quel NSSet? Celui du document courant alors ? Car pour un seul NSValueTransformer, il existe un NSSet pour chaque document ouvert... Quand un document devient actif, j'utilise une méthode de classe pour donner au Transformer le NSSet courant? Mais alors, que devient l'affichage en arrière-plan sur un document inactif?
En tout cas, cela me soulage que tu reconnaisses que c'est compliqué. Pourtant, conceptuellement, cela me paraissait simple...
@Berfis :
Tu as une fenêtre d'affichage commune pour tous tes documents ?
Ou bien tu as une fenêtre d'affichage qui appartient à chaque document ?
ça fait longtemps que je n'ai pas utilisé les NSValueTransformer, mais il me semble que quand on binde sous IB, on fournit seulement la classe du NSValueTransformer, et c'est lui qui l'instancie...
Ceci amène une question: existe-t-il une instance de NSValueTransformer par binding, ou une seule par appli ?
Je ne pensais pas à une méthode de classe, mais vraiment une propriété. Forcément, s'il n'existe qu'une instance du NSValueTransformer, alors utiliser une propriété n'est pas possible. Désolé, je n'y avais pas songé.
Je pense que ce qui complexifie tout, ce sont les bindings. Ils permettent d'obtenir un comportement standard facilement, mais quand ce n'est pas standard (comme ici), il devient intéressant de revenir aux bons vieux data source.
Une autre possibilité serait de changer le problème: de toute façon, ce que tu fais n'est pas standard et sera un peu perturbant pour l'utilisateur. Ce que tu peux faire est de n'afficher QUE les Sentences qui correspondent aux Tests sélectionnés. ça, ça se fait facilement.
(Mon conseil du jour: Quand la spec est trop dure à implémenter, changez la spec).
Oui, et "si ça plante, ce n'est pas un bug, c'est une spécificité" Merci Céroce!
N'afficher QUE les "sentences" appartenant à un "Test" particulier serait en effet trivial, et je suis sûr de pouvoir faire ça avec zéro ligne de code (rien qu'avec des bindings sur le NSSet de chaque test). Je ne vous aurais jamais dérangé pour ça.
Mais mon idée est que l'utilisateur a à tout moment l'ensemble des Sentences sous les yeux, et qu'il les ajoute -ou les retire- d'un Test donné (celui qui est sélectionné, donc). Sentence appartenant déjà au Test = rouge, p.ex. et Sentences hors du Test en gris, p.ex.
Core Data (et le NSSet du relationship, choisi sans doute pour cela d'ailleurs) empêchent par exemple d'inclure deux fois la même Sentence. Et la relation inverse (à quels Tests appartient une Sentence) est automatiquement mis à jour.
Mais Core Data, c'est avant tout une sorte de "ModelKit", et dès qu'on en vient à la partie Vue, c'est délicat. Pourtant, à un moment donné, il faut bien représenter le modèle pour que l'utilisateur puisse agir sur lui. Le NSArrayController, que je suppose être un peu modifié pour la circonstance, est suffisant pour cela, et -même si ce n'est pas très malin- je l'ai souvent dérivé pour lui ajouter des fonctionnalités (contrôle des données, initialisation des attributs, etc.) mais jamais en modifier...
Il existe encore quelques problèmes liés à la mise à jour de l'affichage (des displayIfNeeded qui manquent à certains points-clés) mais l'exemple donné ci-dessus fonctionne. Je suis d'accord cependant que côté design pattern, il est hideux: en effet, on trouve en huit lignes seulement, un mélange complet de MVC... En général, cela trahit une conception floue et des astuces qui vous pètent entre les mains un jour ou l'autre. C'est pourquoi les amis de Cocoa Café voudront bien n'y voir qu'un exemple à ne suivre dans aucun cas...