gestion des delegations
alain.lc
Membre
bonjour
j'ai beaucoup de mal à cerner le fonctionnement des délégations et je ne trouve rien sur le net qui puisse m'aider de façon clair.
un exemple concret avec une appli "utilitaire" sous xcode, elle contient 2 vue (MainView et SlideView) gérer par RootViewController qui permet de deleguer des
actions (toogleView en l'occurence)
ce que je cherche à faire c'est une troisieme vue (testView) qui puisse lancer une fonction contenu dans MainView, et là je m'arrache les cheveux
ou est-ce que je pourrai trouver un tuto qui m'aiderai à comprendre les Delegate ?
d'avance merci
j'ai beaucoup de mal à cerner le fonctionnement des délégations et je ne trouve rien sur le net qui puisse m'aider de façon clair.
un exemple concret avec une appli "utilitaire" sous xcode, elle contient 2 vue (MainView et SlideView) gérer par RootViewController qui permet de deleguer des
actions (toogleView en l'occurence)
ce que je cherche à faire c'est une troisieme vue (testView) qui puisse lancer une fonction contenu dans MainView, et là je m'arrache les cheveux
ou est-ce que je pourrai trouver un tuto qui m'aiderai à comprendre les Delegate ?
d'avance merci
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Le delegate est en soi quelque chose de simple. Lorsqu'une instance ayant un delegate pour un certain nombre de messages reçoit l'un de ces messages, il le fait exécuter à son delegate. C'est tout.
un message = une demande d'exécution de méthode
je pense avoir compris plus ou moins mais c'est encore flou,
dans mon cas mon appli (sur la base d'une template xcode pour iphone) à une vue (MainView et SlideView qui ont deja comme delegue : rootViewControler)
à cela moi j'ajoute testView qui à egalement rootViewControler en delgué ...
tout fonctionne nickel.
Maintenant je cherche à ajouter un DEUXIEME delegué à testView car il faut qu'elle execute une procedure de MainView
et la je melange tout , plus rien ne marche
A quelques exceptions près, un objet ne peut pas avoir de DEUXIEME delegate.
Si tu tentes une telle manip (par code ou dans IB), le premier sera remplacé par le second, ce qui modifiera le comportement de testview.
Le problème est que testView ne connait pas MainView. Là , la gestion standard de delegate ne peut pas te servir. C'est donc à toi d'implanter ta propre gestion d'un second delegate par exemple (ou par notification si ça existe sous iPhone), afin de lier les 2 xxViews.
J'ai l'impression que l'organisation de ton arborescence de vues demande à être repensée !
De plus qu'est-ce qui empêche testView de recevoir le message et de le renvoyer à mainView ?
-( .... ) methodeDansTestView .....
[mainView methodeDansMainView ... ];
Il suffit que testView ait connaissance de mainView en direct ou par l'intermédiaire de rootViewController ...
Bonjour,
Sur mon blog tout neuf il y a une mini-série de deux articles dans lesquels j'essaie d'aborder la délégation pour les non-confirmés. C'est plutôt orienté Mac et pas iPhone, mais je suppose qu'il n'y a pas trop de différence.
C'est ici pour la première partie, et là pour la deuxième partie. J'espère que ça pourra aider...
Effectivement, j'ai revu mon arborescence (ce qui est je pense la meilleure chose à faire dans mon cas), sa fonctionne nickel maintenant.
en tout cas je garde vos conseils pour mes prochaines "delegation"
merci !
cf. le fil asychronisme
C'est tellement simple qu'il n'y a pas de tuto... N'empêche que je ne comprends rien aux delegate, d'autant qu'ils sont toujours masqués par IB !
"Lorsqu'une instance ayant un delegate" : comment savoir si une instance a un delegate ? comment reconnaà®tre un delegate ? comment créer un delegate pour une instance ?
"pour un certain nombre de messages" : où trouver la liste de ces messages ? comment créer un message dans cette liste ?
" reçoit l'un de ces messages," : comment récupérer un tel message ? et le faire exécuter par le delegate ad-hoc ?
"il le fait exécuter à son delegate" : comment ?
"C'est tout" : je pense que ton texte décrit parfaitement le fonctionnement des delegate, mais seulement pour des initiés ; il n'explique pas comment faire !
Un petit exemple de code t'éclaircira peut-être un peu les idées. Voici un exemple de classe bateau qu'on pourrait implémenter et qui utilise un delegate : Voilà un exemple de classe maison "ayant un delegate" et qui appelle la méthode "testClass:valueHasChanged:" de l'objet delegate quand on change sa "value". C'est pour te donner une idée de la façon dont Apple implémente ça dans ses classes aussi, c'est le même principe. Là on "prévient le delegate" en quelques sortes quand l'instance a changé sa valeur (c'est qu'un exemple comme un autre), en appelant la méthode de delegate "[tt]testClass: valueHasChanged:[/tt]", en lui passant l'instance de TestClass en premier argument (comme ça on peut retrouver quel est l'objet TestClass qui a changé sa valeur) et la nouvelle valeur.
Après, si tu as une classe de ce genre et que tu veux l'utiliser, il suffit de prévoir une classe qui implémente la méthode "testClass: valueHasChangedTo:", et de définir le delegate de ta TestClass comme étant l'instance de cette classe qui implémente ta méthode. Exemple : Voilà , avec cet exemple de code qui utilise TestClass, on définit "self" comme delegate de notre TestClass, donc c'est lui qui "recevra" les "delegate methods" quand TestClass les appellera.
----
Après, en pratique, y'a des cas d'utilisation bien plus intéressants de delegate. Par exemple dans tous les cas asynchrones, quand tu demandes de faire une opération, mais qu'elle se termine "plus tard", souvent y'a une méthode de delegate qui est alors appellée "une fois que l'opération est terminée" pour te prévenir que c'est fini.
Exemple, quand tu fais une requête asynchrone avec NSURLConnection (une classe qui permet de faire des requêtes vers des URL, par exemple une requête web, sans bloquer ton code pour autant... asynchrone, quoi). Tu crées une NSURLConnection via la méthode [tt]connectionWithRequest:... delegate:...[/tt] qui va directement lancer la requête de manière asynchrone (donc tu récupères la main ensuite et ton code continue)... Mais du coup comme c'est asynchrone il faut un mécanisme pour te prévenir quand la requête a réussi (ou pas), et que le contenu récupéré par la requête est enfin disponible (puisque ça peut mettre un peu de temps le temps de charger l'URL selon la vitesse de ton réseau, etc)... C'est là qu'intervient le "delegate", que tu as indiqué lorsque tu as créé ton NSURLConnection : c'est à ce "delegate" que va être envoyé la "delegate method" nommée "[tt]connectionDidFinishLoading:[/tt]" (en gros en interne il appelle [tt][delegate connectionDidFinishLoading:self][/tt] j'imagine), donc toi ce que tu as à faire c'est de prévoir d'implémenter la méthode "connectionDidFinishLoading:" dans le code de l'objet que tu as fourni en tant que "delegate" de ta NSURLConnection. Et de mettre dedans le code que tu veux exécuter quand ta requête a abouti et que tu veux traiter les données reçues.
(Bon c'est qu'un exemple, je suis même pas allé voir dans la doc de WebView la signature des vraies méthodes de delegate à implémenter hein, mais c'est pour que tu voies l'idée)
Un délégué, c'est quoi ?
Un objet qui est associé à un autre, qui lui délègue une partie de son fonctionnement.
Par exemple, allez voir la doc de la classe NSWindow. Elle comporte une rubrique "Delegate methods".
Imaginons que je veuille bipper à chaque fois qu'une fenêtre est déplacée. Il faut que mon délégué implémente la méthode windowDidMove:.
Créons, le délégué:
Ensuite, on crée un objet de type OCWindowBeepDelegate sous IB, et on relie l'outlet delegate de la fenêtre à cet objet.
Comment ça fonctionne en pratique:
- si la fenêtre n'a pas de délégué (son outlet n'est reliée à rien), elle exécute elle-même la méthode. Le plus souvent, l'implémentation par défaut ne fait rien.
- sinon, c'est la méthode du délégué qui est appelé.
L'avantage des délégués, c'est que ça évite de sous-classer.
Ce concept (helper objects) est très courant sous Cocoa. On le retrouve par exemple pour les data sources qui remplissent les NSTableView.
Heu t'es sûr ? Moi quand je crée des classes qui utilisent un delegate, je vais comme j'ai fait plus haut, j'appelle juste un [delegate maMethodeDeDelegate] dans ma classe au moment où il faut, pour que "maMethodeDeDelegate" soit appelée sur le delegate..; Et si y'a pas de delegate, autrement dit qu'il est nil, bah ça appelle la méthode sur "nil", donc ça fait rien... Pour toi par défaut à la création de ton objet si tu mets pas de delegate, le delegate est associé à self, l'instance de l'objet lui-même, donc ? Je n'ai jamais pu constater ce comportement, pour moi quand je met pas de delegate personne ne reçoit la méthode (puisqu'envoyée à nil)...
Bonjour,
J'ai une paire d'articles sur la délégation dans mon blog. C'est ici pour le premier et là pour le deuxième.
Je n'arrive pas à retrouver, mais j'ai bien lu dans la doc d'une méthode "l'implementation par défaut fait ça, mais vous pouvez déléguer cette méthode".
On peut faire autrement que mettre l'outlet delegate à self, par exemple:
De toute façon, on peut penser qu'avoir un délégué et posséder une implémentation par défaut est plutôt rare.
C'est très bien, mais tu te précipites sur IB : dès lors on n'a plus aucune chance de rien comprendre...
Je ne pensais pas faire de la pub, je pensais aider. Pardonne-moi si mes interventions ont pu être mal interprêtées.
Pour ce qui est d'IB, j'appuie mon exemple sur le délégué de NSWindow. Je pensais qu'il serait plus simple de mettre en place l'interface graphique avec IB plutôt qu'avec du code. Peut-être aurais-je dû choisir un autre exemple.
Merci en tout cas de ton retour.
Au passage, AliGator, tu confonds délégué et notification, les délégués modifient le comportement des objets délégateurs, alors que les notifications préviennent les objets notifiés que quelque chose est arrivée. L'exemple d'implémentation de délégué que tu donnes n'est pas bon.
Là , le délégateur donnerait plutôt les méthodes : testClass:shouldValueChangeTo: et testClassValueDidChange:
Dans la première, la méthode retournera soit un booléen ou alors la valeur à mettre, et pour la deuxième méthode, le premier argument serait une notification contenant comme propriété "object" l'objet ayant envoyé la notification. Il faut remarquer qu'une notification est à la fois envoyée aux observateurs de la notification mais aussi au délégué de l'objet ayant envoyé la notification.
Dans le cas de NSURLConnection, c'est un peu spécial, le délégué ne va pas forcément modifier le comportement de l'objet, mais il va en fait suivre son délégateur tout au long de la vie de l'objet et être prévenu pour chacune des modifications du comportement qui intervient, c'est pour ça qu'on a la méthode connectionDidFinishLoading: qui ressemble à une notification mais qui n'en est pas une.
Sinon, je me suis amusé à trifouiller un peu avec la méthode windowShouldClose: de NSWindow en faisant une sous-classe. Après quelques tests voilà ce qui en ressort:
- Si le délégué est "nil" ou si il n'implémente pas la méthode windowShouldClose:, la fenêtre a un comportement par défaut, et fait comme si windowShouldClose: retournait YES, en clair la fenêtre se ferme sans que rien d'autre ne se passe.
- Si le délégué implémente la méthode, alors cette méthode est appelée et la fenêtre se ferme si l'implémentation de windowShouldClose: dans le délégué retourne YES. Jusque là pas de problème.
- En revanche, si le délégué est nil ou s'il n'implémente pas la méthode mais que le délégateur implémente cette méthode, alors le délégateur appelle sa propre implémentation et change son comportement selon sa valeur de retour.
Donc, NSWindow n'implémente pas windowShouldClose:, si on implémente la méthode dans une sous-classe ou bien si on ajoute la méthode à NSWindow, cette méthode sera appelée si le délégué ne l'implémente pas.
En clair, une sous-classe, ou une catégorie, peut fournir sa propre implémentation par défaut d'une méthode déléguée... En tout cas pour windowShouldClose:, faut voir après pour les autres méthodes.
Il suffit de voir le nombre de méthodes déléguées "(void)...Will...:(NSNotification*)notification" et qui sont couplées à des notifications.
Un delegate n'est qu'un objet "callBack" et les méthodes déléguées des "callBacks". Et ça intègre le fait de modifier le comportement de l'objet appelant... ou pas.
Les notifications, c'est différent sur plusieurs points très importants :
- ça n'attend pas le retour
- ça peut envoyer un message à un objet qui est totalement inconnu de l'envoyeur
Aussi, les méthodes déléguées ont été créé, et sont là essentiellement pour ça, pour influer sur le comportement de leur déléguant. Pour ce qui est des méthodes déléguées qui retournent une valeur, là c'est évident c'est pour influer sur le comportement du déléguant, et même si la méthode consiste à reproduire le comportement par défaut (genre un return YES; inconditionnel dans -windowShouldClose: ) c'est déjà une influence.
Dans le cas des méthodes déléguées qui ne retourne rien, là c'est plutôt une dérive du concept, cependant, à l'instar des notifications envoyées aux déléguées directement, ça reste en accord avec le concept ; le délégué étant créé pour suivre le délégateur tout au long de son existence, les méthodes telles que connectionDidFinishLoading: de NSURLConnection rentre dans le même panier que les déléguées sans pour autant être des notifications.
http://developer.apple.com/DOCUMENTATION/Cocoa/Conceptual/CocoaFundamentals/CommunicatingWithObjects/chapter_6_section_4.html
Tu ne confonds pas avec DataSource par hasard ?
Les "will" sont aussi appelés par notifications... Ben oui, mais à la base, ce sont des méthodes déléguées ; le fait que ça soit des notifications aussi ne vient qu'après.
C'est appelé sur le délégué pour qu'il ait le temps d'agir avant de rendre la main et que l'action soit effectivement faite. Bref, une callback quoi.
Un exemple parmi d'autres :
Et des exemples de classes qui n'attendent absolument pas en général d'être influencées par leur delegate :
- NSURLConnection
- NSURLDownload
- NSXMLParser
- NSKeyedArchiver
Non, non je parle bien des delegates, et c'est écrit dans la documentation : "
Et c'est confirmé plus bas dans la partie concernant les datasources :
Sinon j'insistait surtout sur l'argument NSNotification, pas sur le will. Toutes les méthodes ayant pour argument NSNotification sont des notifications, et comme le délégué est d'office considérer comme "observeur" de la notification, les méthodes de notification sont aussi passées en méthodes déléguées.
Et je confirme que ce sont des méthodes déléguées, du simple fait que leur premier argument et l'envoyeur du message et non pas une NSNotification.
Je dirais que c'est exactement l'inverse
par ex, l'object delegate de NSWindow est enregistré pour recevoir toutes les notifs émises par NSWindow.
Je confirme, il suffit de court-circuiter les méthodes -addObserver:selector:name:object:, -removeObserver: et -removeObserver:name:object de NSNotificationCenter pour s'en rendre compte...
Quand tu donnes un délégué à une NSWindow, ton objet sera enregistrer comme observeur de toutes les notifications de NSWindow auxquelles il est capable de répondre avec ces fameuses implémentations de méthodes déléguées.
Et quand tu changes de délégué (ou que tu le mets à nil) NSWindow retire la totalité des notifications observables (étant donné que retiré un observeur qui n'est pas dans la liste ne pose pas de problème...).
Donc, si tu veux que ton délégué ne réponde pas aux notifications tout en implémentant les méthodes il te suffit de faire -removeObserver: avec ton délégué en paramètre.
Conclusion, les méthodes prenant une NSNotification en paramètre sont bien des notifications et non pas méthodes déléguées, le délégateur ne fait que simplifier la procédure pour ces méthodes.
Tu soulignes que ce qui t'intéresses dans la phrase, et pas ce qui est le plus important
" coordinating itself with the delegating object OR ... "
Oui, c'est bien la définition d'une callback...
L'objet qui appelle une méthode déléguée en attend le retour pour recevoir l'information :
- Soit une réponse qui lui permet effectivement d'affecter son comportement
- Soit pas de réponse qui lui indique juste que l'objet délégué a bien fini de traiter le message
Contrairement à une notification qui est balancée et on passe à la suite en se fichant de savoir si ça a été bien traité ou pas.
Et pourtant ces méthodes déléguées n'affectent aucunement l'objet qui les appelle contrairement à ce que tu disais au dessus
Elles ne servent que de callbacks de traitement :
" j'ai récupéré ces données pendant le traitement et je les transmets à mon objet callback "delegate" "
Pas quand elles sont envoyées au delegate justement ! Parce que ça attend le retour, et c'est une différence énorme...
Par exemple NSWindow va dire à son "delegate" : "windowWillClose"... Et le delegate va faire tout plein de traitement avant de retourner ; pendant ce temps la fenêtre attendra sagement la réponse.
Alors que pour les autres, elle va balancer la notification, puis fermer sans se préoccuper de ce qu'ils en pensent.
Ce n'est pas qu'une relation d'observation, le "delegate" est actif ! (donc même si l'argument est une NSNotification*, ça ne peut être considéré comme une notification dans le sens des notifications Cocoa).
Le système delegate implanté par les classes cocoa n'est que la traduction "objet" du système des callback utilisés en prog procédurale.
La différence entre delegate et notification est fondamentale.
- pour un delegate, l'objet "émetteur", ce dernier interroge s'il a un delegate et s'il implante la bonne méthode. Si oui, il appelle (call) l'objet delegate et attend la fin de l'exécution de la méthode puisque c'est un call. Eventuellement, le delegate peut retourner une valeur (cas par exemple de windowShouldClose de NSWindow) ou modifier l'objet en paramètre (cas de tableView:willDisplayCell:forTableColumn:row: de NSTableView), d'où le terme de callback.
- pour une notification, l'objet émetteur n'a aucune connaissance du ou DES objets pouvant recevoir les notifs. Pire, la doc Apple indique bien qu'il n'y a aucun synchronisme entre envoi de la notif par l'objet émetteur, et réception par les objets abonnés à la notification.
Et c'est normal, car les notifs passe par la boucle d'événement, alors que les delegate ne sont que des appels purs et simples.
Chaque méthode a donc son avantage :
- un delagate peut (éventuellement) influancer l'émetteur (puisque appel direct).
- une notification peut être reçue par une multitude d'objets en même temps.
-> ce n'est pas une notification (l'appel est direct et attend le retour).
Le fait que l'argument soit de type "NSNotification*" ne prouve absolument pas que ça en soit une (c'est juste une simplification de l'API pour utiliser le même objet à envoyer à son delegate par callback, et en tant que notification pour tous les autres).
Faux faux faux et encore faux, tu n'as pas lu tout ce que j'ai dit !
Pour commencer, les notifications sont envoyés de manière synchrone. Pour que la méthode qui a envoyé la notification puisse continuer son travail, il faut d'abord que toutes les méthodes exécutée par les observeurs aient terminé leur travail.
Ensuite, toutes les méthodes de notification SONT des notifications, déléguées ou pas ! Lorsque tu donnes ton objet comme délégué d'une fenêtre par exemple, l'objet NSWindow va regarder toutes les méthodes prenant une NSNotification comme argument et va enregistrer ton objet comme observeur de ses notifications. Si bien que si tu fais :
La méthode -windowWillClose: ne sera jamais appelée dans ce cas, parce que -setDelegate: aura enregistré self comme observeur de NSWindowWillCloseNotification mais -removeObserver: aura supprimé l'observation.
Donc, il s'agit bien du comportement normal des notifications, et le fait que ce soit désigné comme méthode déléguée ça indique juste que le délégateur enregistrera son délégué comme observeur de cette notification. Mais si l'observation est supprimée, la méthode n'est plus appelée, délégation ou pas.
Oui, il s'agit de la dérive dont je parle, le fait que le délégué se coordonne avec son délégué, mais à l'origine c'était surtout pour modifier le comportement de l'objet, d'ailleurs, la plupart des méthodes déléguées (mise à part les notifications qui n'en sont pas !) modifient le comportement de leur délégateur.
Donc je répète, les notifications sont envoyées de manière synchrone. Sinon les notifications "NSWindowWillMoveNotification" et "NSWindowDidMoveNotification" n'ont pas de sens... La première intervient avant le déplacement de la fenêtre, et la deuxième après, donc si c'était envoyé de manière asynchrone, tu recevrais la deuxième notification alors que la première n'était pas forcément terminée.
De plus, si c'était envoyé de manière asynchrone il faudrait une application multithreadée, et jusqu'à nouvel ordre je ne vois nul part dans la documentation quelque chose disant qu'il faudrait prévoir cette éventualité. C'est-à -dire par exemple qu'il faudrait locker les données, ou s'assurer de leur propreté dans les méthodes appelées par les notifications.
Conclusion, quand un objet poste une notification, il doit attendre que tous les observeurs aient exécuté leur code pour continuer.
Alors là mon cher No, on ne doit pas lire la même documentation !!!!!!!!!!
Donc, si vous lisez la partie Overview de NSNotificationCenter vous pourrez lire ceci:
Ce qui contredit totalement les assertions de Schlum et No comme quoi le notifieur envoie la notification et passe tout de suite à la suite sans se soucier de l'exécution des notifiés !
Bien vu.
Effectivement, ce que j'ai dit plus haut n'est plus d'actualité.
(ce l'était dans de précédentes versions de OSX où j'avais du implanter un système qui suit les mouvements d'une fenêtre, mais le résultat était affreux à cause de cette non-synchronisation qui était à l'époque écrite dans la doc).
A bien relire la doc dont tu as fourni un extrait, je deviens moins affirmatif sur la différence entre delegate et notification.
Merci Psycho pour cette veille informatique.
Ah bon ???
On nous aurait menti