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 :
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.
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(&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.
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
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.
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.
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.
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).
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.
ç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.
Non les variables de classes n'existent pas en ObjC mais on peut les simuler si on veut :
C'est un peu lourd à écrire, il faut impérativement des accesseurs, mais c'est possible.
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.
ç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)
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.
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