Variable de classe, héritage, création de variable à  la volé ?

Salut les gens,



Je suis dans un truc un peu bizarre en ce moment, grosso modo je fait des NSOperation un peu custom qui vont tourner sur des thread spécifiques.



Pour me simplifier la vie (vue que je vais réutiliser le pattern pour pas mal de type d'opé devant avoir chacun leur thread) je suis en train de me triturer l'esprit.



Dans ma CustomOperation j'ai des méthodes de classe qui me renvois un NSThread, le truc classique pour une variable de classe :


<br />
+ (NSThread*)sharedThread {<br />
static dispatch_once_t onceToken;<br />
static NSThread *sharedThreadCustomOperation = nil;<br />
dispatch_once(&amp;onceToken, ^{<br />
  sharedThreadCustomOperation = [[NSThread alloc] initWithTarget:self selector:@selector(sharedThread_mainLoop) object:nil];<br />
  [sharedThreadCustomOperation start];<br />
});<br />
<br />
return sharedThreadCustomOperation;<br />
}<br />




Je cherche donc à  faire en sorte que ce code soit modifié pour chaque sous classe sans pour autant avoir à  réécrire quoi que ce soit. L'objectif étant que chaque sous classe dispose de sa propre sharedThreadCustomOperation.



En somme que la méthode de classe sharedThread soit réimplémenté pour chaque sous classe, de sorte que chacune dispose de sa propre variable static.



Si quelqu'un a une idée, je suis preneur.

Réponses

  • AliGatorAliGator Membre, Modérateur
    Un NSDictionary dont les clés sont les noms de ta classe (et la valeur la sharedInstance associée) ?
  • 'AliGator' a écrit:


    Un NSDictionary dont les clés sont les noms de ta classe (et la valeur la sharedInstance associée) ?




    C'est ce que je voulais éviter... Si je joue avec ce genre de setup c'est pour améliorer les perf. Certes c'est qu'un dico, mais bon peu mieux faire...



    L'idéal serait les templates C++ en fait.
  • AliGatorAliGator Membre, Modérateur
    novembre 2012 modifié #4
    Ouais je vois. Mais si tu comptes implémenter ta méthode sharedInstance une seule fois dans la classe mère et espère que les classes filles vont utiliser toutes cette même implémentation, il faut forcément que tu te bases sur la classe en question pour savoir quelle sharedInstance doit lui correspondre.



    Du coup autre idée : utiliser des associatedObjects sur self (ta classe) pour lui associer le singleton. Du coup plus forcement besoin du dispatch_once puisqu'il suffit de regarder si self (ta classe) a un objc_getAssociatedObject de défini, et si non le créer.


    static char singletonKey;<br />
    <br />
    +(id)sharedInstance<br />
    {<br />
      id obj = objc_getAssociatedObject(self, &amp;singletonKey);<br />
      if (&#33;obj)<br />
      {<br />
        obj = [[[self alloc] init...] autorelease]; // on peut autorelease on va l&#39;associer avec un RETAIN à  notre objet classe il sera retenu<br />
        // tes autres initialisations, ton start, etc<br />
        objc_setAssociatedObject(self, &amp;singletonKey, obj, MACHIN_POLICY_RETAIN);<br />
      }<br />
      return obj;<br />
    }
    Bon c'est du code fait de mémoire tapé sur iPad, j'ai pas testé et je te laisse corriger les typos mais tu vois l'idée.
  • Curieux, je ne suis pas forcément à  jour, mais il me semble que, d'après les documentations, il n'existe pas de variable de classe en objective-C.
  • AliGatorAliGator Membre, Modérateur
    'tablier' a écrit:


    Curieux, je ne suis pas forcément à  jour, mais il me semble que, d'après les documentations, il n'existe pas de variable de classe en objective-C.
    non pas au sens strict du terme, mais là  c'est plus pour parler du concept que yoann évoque ça, justement pour voir comment adapter son besoin spécifique en ObjC où il n'y a pas de variable de classe (et de toute façon je suis pas sur qu'une variable de classe résolverai le problème puis qu'encore faudrait-il que chaque sous-classe la redéfinisse pour lui donner une valeur différente)
  • jpimbertjpimbert Membre
    novembre 2012 modifié #7
    Sinon une habile macro définie dans la classe mère qui prend en paramètre le nom de la classe fille et fabrique la variable de classe et les accesseurs éventuellement, et tant qu'à  faire une partie de la déclaration de la classe fille.

    Je dis ça mais en fait j'aime pas trop les macros, je trouve qu'en abuser peut nuire à  la lisibilité du code (la vrai raison c'est que je ne suis pas très habile en fait).
  • AliGatorAliGator Membre, Modérateur
    Et ça sous-entend d'utiliser la macro dans chaque classe fille que tu définis pour être sur de surcharger la méthode de la classe mère, sachant que si celui qui crée la classe fille oublie ça va pas se voir facilement car va utiliser implémentation mère...



    C'est une solution que je comptais lui proposer mais quand j'ai pensé aux implications et risques de foirer sur un simple oubli dans une sous-classe, je me dis que c'est pas l'idéal.
  • 'AliGator' a écrit:


    Ouais je vois. Mais si tu comptes implémenter ta méthode sharedInstance une seule fois dans la classe mère et espère que les classes filles vont utiliser toutes cette même implémentation, il faut forcément que tu te bases sur la classe en question pour savoir quelle sharedInstance doit lui correspondre.



    Du coup autre idée : utiliser des associatedObjects sur self (ta classe) pour lui associer le singleton. Du coup plus forcement besoin du dispatch_once puisqu'il suffit de regarder si self (ta classe) a un objc_getAssociatedObject de défini, et si non le créer.


    static char singletonKey;<br />
    <br />
    +(id)sharedInstance<br />
    {<br />
      id obj = objc_getAssociatedObject(self, &amp;singletonKey);<br />
      if (&#33;obj)<br />
      {<br />
    	obj = [[[self alloc] init...] autorelease]; // on peut autorelease on va l&#39;associer avec un RETAIN à  notre objet classe il sera retenu<br />
    	// tes autres initialisations, ton start, etc<br />
    	objc_setAssociatedObject(self, &amp;singletonKey, obj, MACHIN_POLICY_RETAIN);<br />
      }<br />
      return obj;<br />
    }
    Bon c'est du code fait de mémoire tapé sur iPad, j'ai pas testé et je te laisse corriger les typos mais tu vois l'idée.




    ça me plait beaucoup ça comme solution ! Faut que je regarde en détail le fonctionnement mais c'est le type de solution que je cherchais !



    Par contre le dispatch_once reste nécessaire à  mon sens pour les problèmes d'opérations concurrentes.
  • 'tablier' a écrit:


    Curieux, je ne suis pas forcément à  jour, mais il me semble que, d'après les documentations, il n'existe pas de variable de classe en objective-C.




    Non les variables de classes n'existent pas en ObjC mais on peut les simuler si on veut :


    <br />
    static dispatch_queue_t MyClass_toto_queue;<br />
    static id MyClass_toto = nil;<br />
    +(void)load {<br />
    static dispatch_once_t MyClass_load_token;<br />
    dispatch_once(&amp;MyClass_load_token, ^{<br />
      MyClass_toto_queue = dispatch_queue_create(&quot;MyClass_toto_queue&quot;, DISPATCH_QUEUE_SERIAL);<br />
      dispatch_async(MyClass_toto_queue, ^{<br />
       MyClass_toto = [NSObject new];<br />
      });<br />
    });<br />
    }<br />
    +(void)setToto:(id)aToto {<br />
    dispatch_async(MyClass_toto_queue, ^{<br />
      MyClass_toto = [aToto retain];<br />
    });<br />
    }<br />




    C'est un peu lourd à  écrire, il faut impérativement des accesseurs, mais c'est possible.
  • Pour mémo voici ma solution, ça marche nickel !


    <br />
    static void const * kSharedQueue_Key;<br />
    +(void)initialize {<br />
    NSOperationQueue *opQ = [NSOperationQueue new];<br />
    [opQ setMaxConcurrentOperationCount:5];<br />
    objc_setAssociatedObject(self, kSharedQueue_Key, opQ, OBJC_ASSOCIATION_RETAIN_NONATOMIC);<br />
    [opQ release];<br />
    }<br />
    + (NSOperationQueue*)sharedOperationQueue {<br />
    return objc_getAssociatedObject(self, kSharedQueue_Key);<br />
    }<br />
  • AliGatorAliGator Membre, Modérateur
    Oui il faut rajouter du thread safety, mais un dispatch_once c'est pas simple dans ton cas car il en faut un différent par classe... on pourrait aussi le mettre en associatedObject mais bon ça commence à  faire lourd ^^



    Ta solution est de créer le singleton dans ton +initialize pour réduire l'accesseur à  un simple return. ça devrait faire l'affaire. Après du coup c'est pas lazy-loaded (mais est-ce bien grave dans ton cas ?) et ça veut dire que tu n'as pas le droit de surcharger +initialize dans tes sous-classes car ça ne marchera plus du coup (et la règle pour +initialize fait qu'on n'appelle pas "super" pour cette méthode particulière il me semble, d'ailleurs je suis même pas sûr que ce soit possible à  ce stade du chargement des classes ?)



    Une autre solution serait de faire le test comme j'ai fait dans mon getter (créer l'instance et faire le set si elle n'existe pas) mais entourer le tout d'un @synchronized(self). Bon du coup c'est un poil en perfs que tu perds, donc tout est une question d'équilibre.
  • AliGatorAliGator Membre, Modérateur
    novembre 2012 modifié #13
    Ta déclaration " static void const * " je suis pas sûr qu'elle soit idéale ?



    ça déclare un pointeur vers du void, mais ce pointeur n'étant pas initialisé explicitement va certainement être mis à  zéro par le compilateur.

    Du coup tu utilises 0 (NULL quoi) comme clé pour ton associatedObject



    ça marche, mais c'est pas conseillé, l'idée de la clé qu'on passe au set/getAssociatedObject c'est d'utiliser une clé unique pour pas risquer de conflit avec un autre associatedObject... si quelqu'un voulait associer un autre associatedObject à  ta classe (self) et utilisait la même technique que toi, à  savoir un "static void const *" initialisé à  zéro par le compilo, il viendrait en conflit avec le tien...



    La pratique habituellement est plutôt de déclarer une variable statique, dont on n'utilise pas le contenu, mais que le compilo va intégrer quelque part dans la section de code du binaire, et d'utiliser son adresse dans le tas, adresse qui sera du coup forcément unique puisqu'il s'agira d'un symbole global. Donc avec cette pratique tu es sûr d'avoir un void* qui vaut une valeur unique (tu sais pas trop quoi mais tu t'en fous du moment que tu utilises toujours la même). Et non de déclarer un pointeur directement.

    Le plus simple étant de déclarer une variable qui prend le moins de place possible (donc un char) : " static const char kYourKeyName; " (A vrai dire on s'en fiche que la variable soit const ou pas on utilise pas son contenu mais son adresse).



    Ou alors faut que tu initialises ton kSharedQueue_Key à  une valeur arbitraire, genre (uintptr_t)0x12345678 (si ARC autorise encore les casts d'entiers vers void* et vice-versa, j'en suis pas si sûr)
  • 'AliGator' a écrit:


    Ta déclaration " static void const * " je suis pas sûr qu'elle soit idéale ?



    ça déclare un pointeur vers du void, mais ce pointeur n'étant pas initialisé explicitement va certainement être mis à  zéro par le compilateur.

    Du coup tu utilises 0 (NULL quoi) comme clé pour ton associatedObject






    C'est une erreur de ma part dans son utilisation en suite, le fait qu'il pointe sur null c'est pas un soucis, ce que je suis censé utiliser c'est &kSharedQueue_Key, l'adresse du pointeur sur rien.
  • AliGatorAliGator Membre, Modérateur
    novembre 2012 modifié #15
    Oui en effet l'important est d'utiliser l'adresse comme clé. S'il pointe sur rien ce n'est pas grave du moment que tu utilises "l'adresse de ce rien" image/biggrin.png' class='bbc_emoticon' alt=':D' />



    J'avais juste eu peur que tu aies déclaré un "void*" en te disant "tiens il attend un pointeur je vais le déclarer directement plutôt que de déclarer une variable et prendre son adresse, c'est plus simple" (alors que ça ne marche pas (enfin ça marche mais si tu fais ça pour tous les associatedObjects ils utiliseraient la même clé NULL).



    Après si tu utilises bien l'adresse alors c'est bon... mais un "char", qui pointe aussi sur '\0' d'ailleurs, prend moins de place qu'un "void*" donc vu qu'il sert à  rien autant qu'il prenne le moins de place possible image/tongue.png' class='bbc_emoticon' alt=':P' /> Et puis en utilisant un char t'aurais une erreur de compilation si tu oublies le "&" à  l'utilisation, donc tu aurais vu ton erreur à  l'usage héhé image/wink.png' class='bbc_emoticon' alt=';)' />
Connectez-vous ou Inscrivez-vous pour répondre.