les Associative References: une inovation qui mériterait qu'on s'y intéresse

ClicCoolClicCool Membre
décembre 2009 modifié dans API AppKit #1
Avec SnowLeopart un certain nombre de nouvelles possibilités font leur apparition.
Si on a pas mal parlé des blocs qui semblent le mériter amplement, je n'ai rien trouvé sur le web (francophone ou anglophone) sur les Associative References.

En gros, elles permettent d'associer un Objet à  un autre (c'est "tout bête" hein ?).
Cela permet, par exemple, d'ajouter de pseudo variables d'instances à  un objet d'une classe sans avoir à  la sous classer.
La Doc d'Apple, Associative References est peu causante, brève et montre juste en exemple l'association d'une NSTring à  un NSArray.


  • En détail l'association est faite par le runTime et il ne faut pas oublier de placer un:
#import <objc/runtime.h>


  • L'association d'un objet à  un autre se fait avec la fonction:

void objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy policy);



- object est l'objet auquel on va en associé un autre
- value est l'objet associé

- La Key doit être unique et est donnée par une indirection, Apple recommande de se baser sur une variable statique et donc dans ce cas on utilisera un:
static char AssociativeKey;


- L'objc_AssociationPolicy ressemble un peu aux attributs de déclaration des properties avec comme valeur possibles de policy:
enum {
  OBJC_ASSOCIATION_ASSIGN = 0,
  OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,
  OBJC_ASSOCIATION_COPY_NONATOMIC = 3,
  OBJC_ASSOCIATION_RETAIN = 01401,
  OBJC_ASSOCIATION_COPY = 01403
};

C'est puissant car, en effet, l'objet associé peut peut être atomic et recevoir un retain et recevra alors un release quand on rompra l'association etc...
(un nouveau casse tête pour les frilleux de la gestion mémoire :P)

  • L'objet associé peut être "récupéré" par:

id objc_getAssociatedObject(id object, void *key)

ATTENTION, le compilateur n'ayant aucun moyen de contrôle du type d'objet renvoyé, il me semble qu'il vaut mieux typer explicitement la valeur de retour de cette fonction. (même si Apple ne le dit pas explicitement)

  • Une autre fonction existe pour supprimer toutes les associations à  un objet:
void objc_removeAssociatedObjects(id object)

Mais Apple en décourage l'usage dans sa doc:
You should not use this function for general removal of associations from objects, since it also removes associations that other clients may have added to the object.


  • On peut donc aussi rompre une association donnée (correspondant à  une clef donnée) en passant simplement nil comme value à  la fonction de création d'une association:

void objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy policy);
  • On peut aussi changer l'objet associé, en se méfiant toutefois du release qui sera envoyé au précédent objet associé si on a pas opté pour OBJC_ASSOCIATION_ASSIGN.

  • On peut aussi, par le biais de plusieurs clefs différentes, associer à  un objet autant d'objets que l'on veut.



A l'usage tout ça fonctionne parfaitement et me semble mériter notre d'intérrêt.

- Ces association pourraient ainsi lever en partie les limitations des catégories, à  savoir l'impossibilité d'ajouter une variable dans une catégorie.

- plus simple encore: Un objet associé n'est en rien modifié et se comporte pareillement en particuliers dans les tests de comparaisons et d'égalité. Ce qui peut simplifier considérablement la vie pour gérer des collections.



Exemple fonctionnel:
Ici, avec seulement 4 lignes de code, on peut utiliser la rapidité et l'unicité des élements d'un Set pour gérer une collection d'ID associées à  la référence vers le controller assigné.
Dans cette appli core-data, le but est de s'assurer qu'une seule fenêtre puisse être ouverte pour chaque objet et de récupérer s'il y a lieu le controller de cette fenêtre.
<br />#import &lt;objc/runtime.h&gt;<br />static char objectCTRLKey;<br />.../...<br />// Ici on veut &quot;ouvrir&quot; tous les object sélectionnés d&#39;un arrayControlleur<br />			for (lobjet in selectedObjects ) {<br />			objetID = [lobjet objectID]; // On récupère l&#39;ID de l&#39;objet<br />			if (! objetIDinSet = [IDOuvertsSet member: objetID]) { // s&#39;il n&#39;est pas référencé dans le set<br />				[IDOuvertsSet addObject: objetID]; // on l&#39;y place<br />				ObjectViewCTRL* objectCTRL = [[ObjectViewCTRL alloc] initWithObjectID: objectID]; // on lance l&#39;ouverture de la fenêtre<br />				objc_setAssociatedObject(objectID, &amp; objectCTRLKey, objectCTRL, OBJC_ASSOCIATION_ASSIGN); // on associe le controlleur à  l&#39;ID qui est dans le set<br />			} else { // L&#39;objet est déjà  en cours d&#39;édition<br />				ObjectViewCTRL *objectCTRL = (ObjectViewCTRL *)objc_getAssociatedObject(objetIDinSet, &amp;objectCTRLKey);<br />				// On fait ce qu&#39;on veut avec le controleur d&#39;édition de l&#39;Objet <br />			}<br />		}		<br />




Et vous ? Quel usage trouvez vous aux Associative References ?



[EDIT] Ajout de la précaution de typecast avec la fonction objc_getAssociatedObject
«1

Réponses

  • 22:32 modifié #2
    Tout simplement excellent, je bookmark ça vite fait! Bravo pour le boulot  :o
  • ClicCoolClicCool Membre
    22:32 modifié #3
    Merci Eagle  :)

    Je trouvait dommage que ça passe inaperçu parmis les nouveautés, pourtant pas si nombreuses, qu'apporte 10.6  8--)
  • ClicCoolClicCool Membre
    octobre 2009 modifié #4
    dans 1256730239:
    .../...
    • L'objet associé peut être "récupéré" par:

    id objc_getAssociatedObject(id object, void *key)
    



    Oups, j'oubliais un détail important.
    Les association étant faites par le runTime, le compilo n'a aucun contrôle sur le type d'objet récupéré.
    Il vaut mieux donc le "typCaster" en dur dans le code.
    Comme dans l'exemple que j'ai donné:
    ObjectViewCTRL *objectCTRL = (ObjectViewCTRL *)objc_getAssociatedObject(objetIDinSet, &amp;objectCTRLKey);
    


    Je me demande si je ne devrai pas éditer mon premier post pour y ajouter cette précision non explicité dans la doc d'Apple ??
    J'le fais ? ? ???
  • 22:32 modifié #5
    Oui  :)
  • Philippe49Philippe49 Membre
    22:32 modifié #6
    Merci ClicCool pour cette info, on va lire à  tête déposée !
    Cela fait penser à  une généralisation des key-value coding compliant container class comme CALayer et CAAnimation.
  • MalaMala Membre, Modérateur
    22:32 modifié #7
    Très intéressant en effet. Merci pour ce topo détaillé ClicCool.  :p :p

    Je trouve que ça fait sauter pas mal de limites des catégories (ajout de variable à  une instance mais aussi accès aux méthodes de la classe mère via super) en se rapprochant de la notion d'héritage multiple.

    Est ce qu'on sait ce qui se passe lorsque deux classes implémentes des méthodes identiques et qu'on les associes?
  • ClicCoolClicCool Membre
    octobre 2009 modifié #8
    @Eagle: C'est fait ;)

    @Louis XVI: Y'a des similitudes en effet dans le fait d'éviter d'ajouter des variables d'instances en sous-classant.
    Par contre les Associative References ne sont pas KVO compliantes telles que.
    Mais en s'en servant conjointement à  une catégorie qui définirait des accesseurs KVO compliant donnant accès à  l'objet associé ça devrait marcher et on pourrait alors utiliser la "dot Syntax" et même aussi s'amuser à  binder sur l'objet associé !  :brule:
  • ClicCoolClicCool Membre
    octobre 2009 modifié #9
    dans 1256736804:
    .../...Est ce qu'on sait ce qui se passe lorsque deux classes implémentes des méthodes identiques et qu'on les associes?


    Strictement parlant on associe pas deux objets ensemble, on associe la référence vers un objet à  un autre.
    L'objet associé n'a aucune référence vers l'objet auquel il est associé.
    Et, sauf si on implémente des méthodes spécifiques (par le biais d'une catégorie par exemple), les deux objets restent indépendants (c'est un aspect qui me plait bien justement) et n'ont pas accès aux iVars ni aux méthodes de l'autre.
  • MalaMala Membre, Modérateur
    22:32 modifié #10
    Ok, j'avais mal compris. Je pensais que l'association était plus fusionnelle que ça.
  • AliGatorAliGator Membre, Modérateur
    22:32 modifié #11
    J'avias déjà  vu une références aux constantes quand j'avais regardé les méthodes du runtime pour les @property, mais je n'avais pas réalisé que c'était une nouvelle fonctionnalité à  part entière..

    J'ai pas eu le temps de tout lire et d'étudier la question par contre, mais quel différences/avantages/inconvénients ça a par rapport à  une classe qui serait auto-KVC un peu comme les CALayers, sur laquelle on pourrait faire [tt]valueForKey:[/tt] et [tt]setValue:forKey:[/tt] pour n'importe quelle clé arbitraire même une qui ne correspond à  aucune variable d'instance ?
  • sekaijinsekaijin Membre
    22:32 modifié #12
    C'est effectivement une nouveauté importante.

    cela rapproche encore plus Obj-C des langages interprétés.

    je n'ai pas connaissance de langage compilé offrant déjà  cette possibilité.

    merci pour l'info
    A+JYT
  • ClicCoolClicCool Membre
    22:32 modifié #13
    dans 1256745044:
    .../...
    J'ai pas eu le temps de tout lire et d'étudier la question par contre, mais quel différences/avantages/inconvénients ça a par rapport à  une classe qui serait auto-KVC un peu comme les CALayers, sur laquelle on pourrait faire [tt]valueForKey:[/tt] et [tt]setValue:forKey:[/tt] pour n'importe quelle clé arbitraire même une qui ne correspond à  aucune variable d'instance ?


    En fait, si j'ai bien compris les CALayers et autres key-value coding compliant container class, ce sont, sur cet aspect, des containers au même titre qu'un Dictionary.

    Ici, comme je le répondais à  Philippe XVI, rien à  voir avec la notion de key-value coding compliant container.
  • AliGatorAliGator Membre, Modérateur
    octobre 2009 modifié #14
    Bon j'ai relu ton premier post.
    Je conçois que ça puisse être utile, mais j'ai encore du mal à  cerner dans quels cas.
    Par exemple, pour le use-case que tu donnes (ton NSSet avec tes ID et tes Controllers associés), je ne vois pas l'intérêt dans le sens où moi j'aurais pas fait du tout comme ça : au lieu d'utiliser un NSSet, j'aurais simplement utilisé un NSDictionary, me servant de l'ID comme clés du dictionnaire et associant le contrôleur en valeur associée à  cet ID.
    Cette solution conserve l'avantage de l'unicité (les clés d'un NSDictionary sont uniques) et en plus est à  mon avis plus performante (bon ça reste à  vérifier mais c'est probable) puisque basé sur une table de hachage.

    for (lobjet in selectedObjects ) {<br />	objetID = [lobjet objectID]; // On récupère l&#39;ID de l&#39;objet<br />	ObjectViewCTRL* objectCTRL = [IDOuvertsMap valueForKey:objetID];<br />	if (!objectCTRL) {<br />		objectCTRL = [[[ObjectViewCTRL alloc] initWithObjectID: objectID] autorelease]; <br />		[IDOuvertsMap setValue:objectCTRL forKey:objetID];<br />	}<br />	// On fait ce qu&#39;on veut avec le controleur d&#39;édition de l&#39;Objet <br />}
    
    Code que, personnellement, je trouve plus compréhensible (mais bon c'est une affaire de goût ça aussi, j'admet). C'est aussi pour ça que je te faisais le parallèle avec les "KVC-compliant container classes"... Ou alors j'ai toujours pas tout compris... ;D

    Après, au sens association pure, genre le cas du "overview" de l'exemple Apple, je comprend le sens que ça peut avoir que cette association, mais j'ai du mal à  en trouver des use-cases pratiques pour l'instant...?
  • ClicCoolClicCool Membre
    octobre 2009 modifié #15
    dans 1256760175:
    .../...
    Par exemple, pour le use-case que tu donnes (ton NSSet avec tes ID et tes Controllers associés), je ne vois pas l'intérêt dans le sens où moi j'aurais pas fait du tout comme ça : au lieu d'utiliser un NSSet, j'aurais simplement utilisé un NSDictionary, me servant de l'ID comme clés du dictionnaire et associant le contrôleur en valeur associée à  cet ID.
    Cette solution conserve l'avantage de l'unicité (les clés d'un NSDictionary sont uniques) et en plus est à  mon avis plus performante (bon ça reste à  vérifier mais c'est probable) puisque basé sur une table de hachage.../...


    C'est ce que je faisait avant en effet.
    Je reste persuadé que le set est plus performant néanmoins.

    Plus encore, dans les cas où j'aurais besoin de trier mes ID en les mettant dans un Array, c'est la seule solution élégante.
    Les clefs d'un dictionnaire sont uniques mais il n'est pas rapide de trier un dictionnary par ses clefs ;)

    Et enfin c'est plus évolutif, et quand l'application nécessitera que j'associe aussi un TimeStamp d'ouverture ou tout autre information, je n'aurais qu'à  associer ces objets d'information à  l'objetID qui restera toujours aussi facile à  gérer dans son Set (ou son Array...).


    [EDIT] P.S.: Les sets aussi sont basés sur une table de hashage
  • ClicCoolClicCool Membre
    22:32 modifié #16
    Par ailleurs, au delà  de mon petit usage de test-exemple perso, les Associative Références offrent de nouveaux horizons à  l'usage des catégories quand on ne veut (peut) pas sous-classer une classe donnée ;)
  • AliGatorAliGator Membre, Modérateur
    22:32 modifié #17
    Je ne comprend pas ta dernière phrase de ton avant-dernier post : "si je veux trier mes ID en les mettant dans un Array" : tu veux dire les stocker (de façon pérenne côté métier) dans un NSArray ? Mais alors si tu fais ça, tu perds la contrainte d'unicité de tes objectID, non ?

    Enfin, bon, après, comme je le disais, ton use-case ne me semble pas des plus pertinents, en tout cas moi je ne vois pas un avantage si énorme à  ces Associative References dans ce genre de cas là  (d'autant que du coup je trouve moins clair de faire des appels à  des méthodes bas niveau et dépendantes du Runtime quand on a une solution "plus Objective-C et objet / KVC" à  disposition.

    Maintenant, dans d'autres cas, comme simuler l'ajout de variables d'instances via une catégories d'une classe, pourquoi pas. Ca commence à  être alors alambiqué comme architecture logicielle et façon de faire, mais bon, si jamais on n'a pas le choix... :P
  • ClicCoolClicCool Membre
    22:32 modifié #18
    Mon exemple de code n'avait pas d'autre prétention que d'être un "exemple fonctionnel" et avait également l'avantage de bien montrer qu'un object auquel un autre est associé n'est pas fondamentalement modifié. (Le Set le considèrant identique à  un autre de même "valeur").

    Et ce thread n'a pas non plus vocation à  faire l'éloge inconditionelle des Associative References, mais plus simplement d'attirer l'attention sur cette nouveauté que beaucoup semblaient ne pas même avoir remarquée.

    Après, ce qui m'intéresse ici c'est le point de vue de chacun sur l'intérêt, les avantages et inconvénients de cette nouveauté et sur la place qu'elle pourrait être amenée à  prendre dans nos projets.




    P.S. ça me rapèle un peu le temps de l'apparition des Bindings que beaucoups ont dénigré pendant des années avant de leur accorder une place dans leur pratique.


    P.P.S. j'avais édité le post auquel tu semble faire référence.
    Bien dommage que le forum n'affiche pas la traditionnelle anotation donnant la date/heure de la dernière édition.
  • Philippe49Philippe49 Membre
    octobre 2009 modifié #19
    Je trouve très pratique la possibilité d'ajout de variables dans les CALayer et CAAnimation, notamment pour les delegate methods ou l'animation est passée en argument. On peut passer avec l'animation un ou plusieurs objets par association, et cela évite de sous-classer.
    Pareil avec le CALayer, on peut lui donner un identifier par exemple.

    Donc cette possibilité d'associer à  n'importe quelle classe un objet me semble intéressante. Notamment pour les classes non sous-classables comme NSString. Jusqu'à  maintenant, on faisait un wrapper, là  on a une solution moins lourde. Je teste cela.
    Effectivement, je suis d'accord avec toi Ali, une syntaxe "Cocoa" serait quand même plus sympathique.  Qu'entends-tu par "méthodes dépendantes du Runtime" ?
  • AliGatorAliGator Membre, Modérateur
    22:32 modifié #20
    Oui, c'est vrai que pour les CALayers j'utilise aussi souvent la possibilité d'ajouter des valeurs à  des clés arbitraires, pour identifier mes CALayers dans mes animations et leur passer des paramètres.
    Dans ce sens là , les associations semblent en effet intéressantes pour des cas similaires sur des classes ne supportant pas le "KVC-compliant container class" comme CALayer.

    Sinon par "méthodes dépendantes du runtime" je me suis mal exprimé, je voulais pas dire dépendantes, juste "méthodes bas niveau du runtime". Bon elles sont dépendantes du runtime utilisé (Objective-C 1.0 ou 2.0), mais bon en pratique on s'en fiche un peu maintenant tout le monde utilise le dernier RunTime ;)

    Après, au même titre que j'ai fait une catégorie de NSObject pour ma méthode releasePropertiesForObject (cf mon Truc & Astuce dans l'autre partie du forum), on peut faire un méthode [tt]setAssociatedObject:forKey:[/tt] dans une catégorie de NSObject pour rendre ça plus user-friendly à  coder. Car j'avoue que ce genre de syntaxe C avec ces noms de fonctions, je suis pas fan :P
  • ClicCoolClicCool Membre
    22:32 modifié #21
    dans 1256809160:
    .../...Après, au même titre que j'ai fait une catégorie de NSObject pour ma méthode releasePropertiesForObject (cf mon Truc & Astuce dans l'autre partie du forum), on peut faire un méthode [tt]setAssociatedObject:forKey:[/tt] dans une catégorie de NSObject pour rendre ça plus user-friendly à  coder. Car j'avoue que ce genre de syntaxe C avec ces noms de fonctions, je suis pas fan :P


    ça c'est une idée qu'elle est bonne !  :o
  • Philippe49Philippe49 Membre
    octobre 2009 modifié #22
    Que pensez-vous de ce petit exemple de débutant ?
    L'idée est d'associer ou non une qualité à  un objet.

    <br />#import &lt;Foundation/Foundation.h&gt;<br />#import &lt;objc/runtime.h&gt;<br /><br />static&nbsp; NSString * Mali=@&quot;Mali&quot;; <br />static&nbsp; NSString * Burkina=@&quot;Burkina Fasso&quot;;<br />static&nbsp; NSString * Ethiopia=@&quot;Ethiopie&quot;;<br />static&nbsp; char countryKey;<br /><br />@interface NSString (cities)<br />+(NSString *) stringWithCity:(NSString *)city inCountry:(NSString *) country;<br />@end<br /><br />@implementation NSString (cities)<br />+(NSString *) stringWithCity:(NSString *)city inCountry:(NSString *) country{<br />	NSString * string=[NSString stringWithString:city];<br />	objc_setAssociatedObject(string,&amp;countryKey,country,OBJC_ASSOCIATION_COPY);<br />	return [[string retain] autorelease];<br />}	<br />@end<br /><br /><br />int main (int argc, const char * argv&#91;]) {<br />	NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];<br /><br />	<br />	NSString * city0=[NSString stringWithCity:@&quot;Bamako&quot; inCountry:Mali];<br />	NSString * city1=[NSString stringWithCity:@&quot;Bobodioulasso&quot; inCountry:Burkina];<br />	NSString * city2=[NSString stringWithCity:@&quot;Ouagadougou&quot; inCountry:Burkina];<br />	NSString * city3=[NSString stringWithCity:@&quot;Addis Abeba&quot; inCountry:Ethiopia];<br />	NSString * city4=[NSString stringWithCity:@&quot;Diredawa&quot; inCountry:Ethiopia];		<br />	NSString * city5=[NSString stringWithCity:@&quot;Tombouctou&quot; inCountry:Mali];<br /><br />	NSString * city6=[NSString stringWithString:@&quot;Ushuaia&quot;];<br />	<br />	NSArray * cities=[NSArray arrayWithObjects:city0,city1,city2,city3,city4,city5,city6,nil];<br />	<br />	for(NSString * city in cities){<br />		NSString * country=(NSString*) objc_getAssociatedObject(city,&amp;countryKey);<br />		if([country isEqualToString:Ethiopia]){<br />			puts((char *)[city UTF8String]);<br />		}<br />	}<br />	<br />	[pool drain];<br />	return 0;<br />}<br /><br />
    


    Ce qui n'est pas clair pour moi pour l'instant c'est la clé. Où va-t-on la mettre dans un projet ?
    Dans l'unité de compilation où on fait l'association .. cela limite la portée de l'association ??
  • ClicCoolClicCool Membre
    22:32 modifié #23
    Pour les clefs je me pose aussi des questions.
    Les Associative References majorent encore un peu le coté dynamique de l'ObjC, et en même temps il me semble que ces clefs en limite la souplesse.

    Ton exemple m'intéresse car de mon côté je partais plus sur des methodes d'instances de type Setter associant un objet quelquonque au receveur et Getter renvoyant l'objet lié au receveur.

    Pour ton exemple tu est donc parti sur une méthode de classe bien propre qui renvoie un nouvel objet bien propre lié à  un autre.
    Mais comment sera exploitée cette référence après ?
  • ClicCoolClicCool Membre
    octobre 2009 modifié #24
    En fait, en ce qui concerne les clefs, ça ne semble pas si limitatif.
    La doc dit juste:
    The key is a void pointer. The key for each association must be unique. A typical pattern is to use a static variable

    En gros, faut qu'elles soient uniques.
    Le fait qu'elles soient déclarées en statique est juste une juggestion d'usage typique.

    Je viens de faire un test rapide en balançant un pointeur sur une NSString créée chaques fois juste avant l'appel aux fonction avec un simple
    NSString* clef = @&quot;maClefUnique&quot;
    
    et ... ça a l'air de marcher correctement à  première vue et j'associe et récupère les bons objets selon le contenu de la NSString !  :o

    Ce serait la porte ouverte à  l'implémentation de methodes KVO genre setValue: (id) value forKey: (id) key etc ...
    Je tenterais ça dès que possible :)
  • Philippe49Philippe49 Membre
    octobre 2009 modifié #25
    dans 1256843031:

    En gros, faut qu'elles soient uniques.
    Le fait qu'elles soient déclarées en statique est juste une juggestion d'usage typique.

    Oui, qu'elles soient uniques pour un objet donné.
    Le fait d'autoriser le cas non static donne la possibilité d'avoir une visibilité de cette clé dans l'ensemble d'un projet. Bien.
    En fait la clé ne semble servir que par son adresse, et on utilise le compilateur pour nous fournir des adresses sûrement différentes.  Ok ?

    dans 1256841892:

    Mais comment sera exploitée cette référence après ?

    Difficile de construire l'exemple à  partir de la solution. L'idée de l'exemple est la possibilité d'adjoindre une sorte de type perso, une qualité à  un objet. La qualité liée à  countryKey pourrait d'ailleurs tout à  fait s'associer à  une NSString une NSMutableString, un NSNumber (deux qualités:c'est une population+c'est un pays), une collection(c'est un ensemble de villes du même pays), un NSData(ce data décrit les données d'un pays), une classe perso(un pays). Maintenant, cela ouvre des horizons, à  voir si cela simplifie la lecture d'un code.   
  • AliGatorAliGator Membre, Modérateur
    22:32 modifié #26
    @toto retourne un pointeur vers une NSString constante "toto" (créée dans la partie "text" du binaire), il me semble. C'est à  dire que où que tu mettes @toto dans ton programme, et même si tu le mets plusieurs fois à  des endroits différents, il me semble bien que ça retournera toujours le même pointeur au sein d'un même binaire: c'est optimisé à  la compilation pour n'avoir qu'une seule fois "toto" dans le binaire, une sorte du coup de "static binary-wide".

    Problème 1 : Si tu des NSString non pas statiquesmais créées avec stringWithFormat... tu as beau passer la même clé au sens "même chaà®ne", ça ne marchera pas. Bon en même temps c'est compréhensible, mais c'est juste qu'il ne faut pas penser à  la clé comme celles utilisées par exemple dans un NSDictionary (qui utilises isEqual pour leur comparaison, elles)

    Problème 2 : Si tu utilises autre chose que des NSString à  la limite c'est pareil : c'est pas utilisé avec isEqual pour trouver le bon objet associé à  la bonne clé, mais bien une égalité pure

    Problème 3 : Si tu passes tes objets entre diverses librairies ou divers frameworks de ton application, je ne suis pas sûr que les objets statiques passent "au travers" de ce passage inter-librairies. En particulier si tu associes un objet à  ton objet dans ton appli, et que tu veux récupérer l'objet associé depuis une autre librairie...

    Après, je me demande comment il se sert de ce void*. Etant donné que pour lui c'est vraiment un void* au sens où il ne sais vraiment pas ce que c'est, il ne va pas manipuler l'objet ou la mémoire que l'on lui passe, genre il ne va pas essayer d'appeler une fonction sur l'objet passer pour retrouver le associatedObject, ça me parait évident. Donc pour moi il utilise uniquement le pointeur (l'adresse de l'objet, pas sa valeur). Du coup je pense que rien ne nous empêche de passer un entier quelconque (casté en void* bien sûr)... Et là  du coup plus simple de manipuler tout ça sans se poser de question de portée des variables statiques utilisées...
  • Philippe49Philippe49 Membre
    22:32 modifié #27
    dans 1256845327:

    Problème 1 : Si tu des NSString non pas statiquesmais créées avec stringWithFormat... tu as beau passer la même clé au sens "même chaà®ne", ça ne marchera pas. Bon en même temps c'est compréhensible, mais c'est juste qu'il ne faut pas penser à  la clé comme celles utilisées par exemple dans un NSDictionary (qui utilises isEqual pour leur comparaison, elles)

    oui manifestement, les clés  (countryKey dans l'exemple) ne sont pas traitées par isEqualToString mais par une simple comparaison d'adresse, le isEqual de base.


    dans 1256845327:

    Après, je me demande comment il se sert de ce void*. Etant donné que pour lui c'est vraiment un void* au sens où il ne sais vraiment pas ce que c'est, il ne va pas manipuler l'objet ou la mémoire que l'on lui passe, genre il ne va pas essayer d'appeler une fonction sur l'objet passer pour retrouver le associatedObject, ça me parait évident. Donc pour moi il utilise uniquement le pointeur (l'adresse de l'objet, pas sa valeur). Du coup je pense que rien ne nous empêche de passer un entier quelconque (casté en void* bien sûr)... Et là  du coup plus simple de manipuler tout ça sans se poser de question de portée des variables statiques utilisées...

    Tout à  fait d'accord, simplement le fait de l'associer à  une variable permet d'une part la clarté dans le code, assurer peut-être un traitement en arrière de l'ensemble de ces keys (le compilateur ne fait-il pas un tableau de ces keys ?), assurer que ces références de clés (=adresses) sont différentes.
  • ClicCoolClicCool Membre
    22:32 modifié #28
    Je trouve pas la doc très claire et ne la lit peut-être pas comme toi
    dans 1256844224:

    dans 1256843031:

    En gros, faut qu'elles soient uniques.
    Le fait qu'elles soient déclarées en statique est juste une juggestion d'usage typique.

    Qu'elles soient uniques pour un objet donné.

    Que veux tu dire par "pour un objet donné" ?
    Si c'est qu'un objet donné ne peut avoir qu'un seul autre objet associé avec cette clef OK
    Mais la même clef peut servir à  d'autres objets pour qu'eux aussi aient un objet (unique) associé via cette clef.
    dans 1256844224:

    Le fait d'autoriser le cas non static donne la possibilité d'avoir une visibilité de cette clé dans l'ensemble d'un projet.
    En fait la clé ne semble servir que par son adresse, et on utilise le compilateur pour nous fournir des adresses sûrement différentes.


    C'est pour tester ça justement que je construisais chaque fois une nouvelle NSString à  la volée juste avant l'appel des fonctions.
    Mais il est vrai que ma façon d'initialiser ma NSString avait toutes les chances de renvoyer sur la même adresse.
    J'ai donc testé avec 2 clefs différentes en associant 2 objets différents au même objet. Et j'ai initialisé mes clefs ainsi:
    <br />		NSMutableString * clef1 = [[NSMutableString alloc] initWithString:@&quot;clef&quot;];<br />		[clef1 appendString:@&quot;1&quot;];
    

    // Association ou récupération d'un Objet
    NSMutableString * clef2 = [[NSMutableString alloc] initWithString:@clef];
    [clef2 appendString:@2];
    // Association ou récupération d'un autre Objet[/code]
    Les clefs sont déclarées en locale et initialisées juste la ligne avant avant les fonction objc_setAssociatedObject et objc_getAssociatedObject.
    L'association se passe bien et je récupère les bons objets selon la valeur de la  NSString crée en clef.
    Sur ce coup il me semble que l'adresse n'est pas la même chaque fois et que c'est l'identité de la mémoire pointée qui est testée non ?

  • ClicCoolClicCool Membre
    octobre 2009 modifié #29
    Oups, j'avais pas vu ton post

    dans 1256845327:
    .../...Du coup je pense que rien ne nous empêche de passer un entier quelconque (casté en void* bien sûr)... Et là  du coup plus simple de manipuler tout ça sans se poser de question de portée des variables statiques utilisées...

    Oui ça marche.
    J'ai essayé hier avec plusieurs clefs déclarées ainsi:
    static char clef1 = &#39;A&#39;;<br />static char clef2 = &#39;B&#39;;
    

    sans problème

    [EDIT] ce qui prouve rien tant que j'aurais pas ajouté un:
    static char clef3 = &#39;A&#39;;
    

    pour vérifier si c'est l'adresse ou le "contenu" ...
  • AliGatorAliGator Membre, Modérateur
    22:32 modifié #30
    Bah justement d'après les exemples d'Apple que vous avez recopié ici, la variable statique n'est même pas initialisée, si ?
    En tout cas j douterai vraiment fortement que ce soit le contenu qui soit utilisé, ça n'aurait pas de sens. C'est vraiment juste un "==" entre le void* qu'on passe dans "objc_getAssociatedObject" et la liste de toutes les autres clés void* dans lesquelles il cherche.

    Pour moi ils auraient pu vraiment tout aussi bien utiliser un [tt]long long int[/tt] plutôt qu'un void* puisqu'en interne ils font qu'une comparaison sur l'adresse, donc juste un entier en qques sortes. Avoir mis un type "void*" c'est juste pour nous permettre de pouvoir passer n'importe quoi à  la fonction (et nous inciter à  passer un pointeur vers un objet ou autre) sans avoir à  "caster". Enfin j'imagine mais tout semble indiquer que ça marche comme ça ;)
  • Philippe49Philippe49 Membre
    octobre 2009 modifié #31
    dans 1256849079:

    Bah justement d'après les exemples d'Apple que vous avez recopié ici, la variable statique n'est même pas initialisée, si ?

    Non elle n'est pas initialisée, c'est simplement l'adresse de cette clé qui est utilisée. Le contenu est complètement inutile.
    Effectivement static int countryKey, ou int countryKey , ou struct {char name[10]; void * data;} countryKey={"trucmoche",NULL}; c'est pareil que static char countryKey;


    dans 1256847147:

    Qu'elles soient uniques pour un objet donné.

    Que veux tu dire par "pour un objet donné" ?
    Si c'est qu'un objet donné ne peut avoir qu'un seul autre objet associé avec cette clef OK

    Oui c'est cela que je voulais dire, l'unicité de la clé pour l'association avec un objet donné.




Connectez-vous ou Inscrivez-vous pour répondre.