Question pour les pro de l'objet sous obj-c

al33eral33er Membre
00:21 modifié dans API UIKit #1
Bonjour,

Je voudrais savoir comment on peut balayer les instances d'une classe sous obj-c et pour chaque instance appliquer une méthode en fonction d'une valeur contenue dans une propriété de cette instance.

J'ai un peu répété le mot instance mais c'est pour bien qualifier mes propos.

Je vous remercie de votre aide par avance .

Alexandre.

Réponses

  • ChachaChacha Membre
    novembre 2008 modifié #2
    dans 1227649051:

    Bonjour,

    Je voudrais savoir comment on peut balayer les instances d'une classe sous obj-c et pour chaque instance appliquer une méthode en fonction d'une valeur contenue dans une propriété de cette instance.

    J'ai un peu répété le mot instance mais c'est pour bien qualifier mes propos.

    Je vous remercie de votre aide par avance .

    Alexandre.


    A priori, le run-time Objective-C n'a aucune raison d'enregistrer lui-même les références aux instances créées; ce serait même plutôt déconseillé, puisqu'alors, les objets ne seraient jamais détruits, retenus qu'ils seraient dans une structure de données.
    Il faut donc faire tout le travail toi-même.
    Si tu utilises le Garbage Collector, utiliser une structure à  références __weak (telle NSHashTable) marcherait. Sinon un bête tableau C.
    Je propose cette solution :



    @implementation Toto //dans la class Toto

    //conteneurs globaux pour tes instances:
    static Toto**   tableauDeToto;  //si pas de garbage collector
    static NSHashTable* tableauDeTotoGC; //si garbage collector

    +(void) initialize
    {
      @synchonized(self) //Schlum nous optimiserait tout ça avec deux ifs (http://www.objective-cocoa.org/forum/index.php?topic=2778.msg27655#msg27655)
      {
        if (!tableauDeToto) //si pas de GC
           allouer(tableauDeToto); //avec malloc() par exemple. Pourquoi pas un std::list en objective-C++ ?
        if (!tableauDeTotoGC)
          tableauDeTotoGC = [[NSHashTable alloc] init];
      }
    }

    -(id) init //dans l'initilisateur désigné
    {
      if (![super init])
        return nil;
      [self register:self]; //stocke l'objet avec une référence faible
      return self;
    }

    -(id) dealloc
    {
      [self unregister:self]
      [super dealloc];
    }


    on aurait alors


    -(void) register:(Toto*)toto
    {
      @synchronized(tableauDeToto)
      {
         ajouter(tableauDeToto, toto); //avec realloc par exemple.
         [tableauDeToto addObject:toto]; //si GC
      }
    }

    -(void) unregister:(Toto*)toto
    {
       ...bon, tu as compris le principe...
    }

    @end


    Ensuite, libre à  toi de parcourir tableauDeToto et d'y faire ce que tu veux...

    +
    Chacha
  • AntilogAntilog Membre
    00:21 modifié #3
    Je ne suis pas un pro, donc...

    Je ne connais aucun moyen de connaà®tre les instances d'une classe!

    Je crois même qu'il n'y en a pas (sauf à  hacker le runtime objective-C)
  • 00:21 modifié #4
    Il me semble que dans la logique de l'objective-c chaque instances devrait plutôt s'enregistrer au centre de notification et répondre ensuite au post de la dite notification... Voir NSNotificationCenter.
  • CéroceCéroce Membre, Modérateur
    00:21 modifié #5
    Euh, les gars, je crois que vous êtes un peu parti en live, là ...
    Je pense que ce que veux dire Al33er c'est:

    - j'ai un array contenant des instances d'une même classe
    - pour chaque instance, j'appelle une méthode -[propriete] qui me renvoie une valeur
    - selon la valeur, j'appelle une méthode ou une autre.

    Al33er, peux-tu confirmer ou infirmer ce que je viens d'écrire ?
  • NseaProtectorNseaProtector Membre
    00:21 modifié #6
    Merci Céroce, j'osais pas le dire mais moi aussi j'avais compris cela, et au vue des réponses je me suis dis:
    Oula, ferme là  ça devient bien complexe !!!!
    Au risque de dire une connerie, il pourrait implémenter une méthode avec un "if" tout bête, non ?
    Genre si c'est une instance de "NSObjet",
    il créé une classe "MonObjet" de type "NSObjet"
    il ajoute la méthode "SiMaPropriete"
    et au lieu de créer des instances de "NSObjet" il créer des instances de "MonObjet"
    Je me trompe certainement mais je risque juste d'apprendre de mes erreurs et comprendre de mieux en mieux...

  • ChachaChacha Membre
    00:21 modifié #7
    dans 1227684624:

    Il me semble que dans la logique de l'objective-c chaque instances devrait plutôt s'enregistrer au centre de notification et répondre ensuite au post de la dite notification... Voir NSNotificationCenter.


    D'un côté c'est pas faux (NSNotificationCenter utilise une structure __weak avec GC).
    Mais en même temps, il vaut peut-être mieux  ne pas alourdir le NSNotificationCenter avec toutes les instances d'une classe pour un cas si particulier ?

    Euh, les gars, je crois que vous êtes un peu parti en live, là ...

    Bah je pense pas; sinon y a pas de difficulté, c'est pas drôle.

    On va bien voir quand il repassera !

    +
    Chacha
  • psychoh13psychoh13 Mothership Developer Membre
    00:21 modifié #8
    dans 1227649051:
    Je voudrais savoir comment on peut balayer les instances d'une classe sous obj-c et pour chaque instance appliquer une méthode en fonction d'une valeur contenue dans une propriété de cette instance.

    Alors, moi je peux voir deux trucs dans ce qu'il dit :
    Soit, il veut envoyer un message à  plusieurs objets et que ces objets se servent de leur variables d'instance pour répondre au message.
    Soit, il veut envoyer un message à  plusieurs objets pour leur dire de s'envoyer un message qui leur est spécifié dans l'une de leur variable d'instance...

    Dans le deux cas, une methode commune à  tous les objets suffie.

    Après il faut savoir quels objets il veut atteindre, s'il veut atteindre un certain nombre d'objets, il utilise un NSArray... S'il veut atteindre tous les objets d'une même classe, il suffit que dans son -init designé il enregistre l'objet dans un tableau global sur lequel il enverra les messages.

    Maintenant il faut savoir plus précisément ce qu'il veut.
  • NseaProtectorNseaProtector Membre
    novembre 2008 modifié #9
    En clair, la question est mal posée pour que chacun comprenne se qu'il veux, non ?
    Perso, balayer une instance en plein hivers alors qu'on pourrait passer un coup de chasse neige...
  • al33eral33er Membre
    00:21 modifié #10
    je me réexplique pour essayer d'être plus clair  et je précise donc :

    Au chargement de mon application, j'appelle deux fois une méthode qui me lance un timer sur une date passée en paramétre.

    A l'appuie sur un bouton, je reprogramme les timer, pour cela je dois invalider les timer existants pour eviter qu'ils se décenche et ainsi les reprogrammer.

    Je voulais donc  :

    Balayer les instances vivantes de la classe NSTIMER et quand ils sont valident les invalider.

    Voila mon besoin.

    Ne vous torturez pas l'esprit. Si la méthode n'existe pas on va faire autrement.

    Peut être que la solution suivante fonctionnerait, je vais tester ce soir :

    Récupérer l'instance du timer créé par la méthode et ainsi agir dessus par son nom.



    Cordialement.

    Alexandre.
  • psychoh13psychoh13 Mothership Developer Membre
    00:21 modifié #11
    Si tu n'as jamais plus de deux instances de NSTimer, tu fais deux variables d'instance contenant les NSTimer et tu les invalides si besoin...
    Si tu en as un nombre indéfini, utilise un NSArray.

    Pas la peine de tortiller du cul pour chier droit dans un escalier en colimaçon...
  • AliGatorAliGator Membre, Modérateur
    novembre 2008 modifié #12
    Ben heu oui pourquoi se triturer l'esprit ? Tu as besoin d'accéder à  tes timers, garde les en variable d'instance, directement ou dans un tableau, c'est la procédure standard pour ça... :P

    D'autant que ça serait dangereux d'envoyer un message à  tous les NSTimers sans distinction, car si tu utilises un framework qui a sous le capot des NSTimers, ou d'autre code qui n'a rien à  voir avec ton bout de code mais utilise des NSTimers aussi (alors qu'en plus tu les "vois" pas forcément ils peuvent être "cachés" dans le code des frameworks que tu utilises....) tu risquerais du coup de faire des boulettes...

    En plus, si tu utilises un NSArray, tu peux appeler une méthode sur tous les éléments de ton NSArray, avec makeObjectsPerformSelector:@selector(invalidate) par exemple pour tous les invalider d'un coup en une ligne...
  • NseaProtectorNseaProtector Membre
    novembre 2008 modifié #13
    le .m
    //Les variables a déclarer:<br />//NSTimer *timer;<br />//double interval;<br />- (IBAction)timerDo:(id)sender <br />{ <br />	<br />    if (timer == nil) { <br />        NSLog(@&quot;Si le timer n&#39;existe pas on le créer&quot;); <br />		NSLog(@&quot;Interval: %f&quot;,interval);<br />        // Interval est un double qui contient la durée du timer<br />        timer = [[NSTimer scheduledTimerWithTimeInterval:interval<br />												  target:self <br />												selector:@selector(doTimer:) <br />		//doTimer est la fonction appellé par le timer<br />												userInfo:nil <br />												 repeats:YES] retain]; <br />    } else { <br />        NSLog(@&quot;Stop the Timer&quot;); <br />        // On invalide et on libère le timer<br />        [timer invalidate]; <br />        [timer release]; <br />        timer = nil; <br />		//On en crée un nouveau<br />		timer = [[NSTimer scheduledTimerWithTimeInterval:interval<br />												  target:self <br />												selector:@selector(doTimer:) <br />				  //doTimer est la fonction appellé par le timer<br />												userInfo:nil <br />												 repeats:YES] retain]; <br />		<br />    } <br />} <br />- (void)doTimer:(NSTimer *)aTimer <br />{ <br /><br />		NSLog(@&quot;Coucou&quot;);<br /><br />}<br />
    


    Ne pas oublier de déclarer les méthodes et de "Binder" le bouton a la fonction timerDo.

    Le .h
    <br />@interface AppController : NSObject <br />{	<br />	NSTimer *timer;<br />	double interval;<br />}<br />-(IBAction)timerDo:(id)sender;<br /><br />@end<br />
    
  • psychoh13psychoh13 Mothership Developer Membre
    00:21 modifié #14
    dans 1227727079:

    Ne pas oublier de déclarer les méthodes et de "Binder" le bouton a la fonction timerDo.
    Ce code doit donc être dans un "AppController"


    Et voilà  la version nettoyée de tout ce qui est inutile :

    //Les variables a déclarer:<br />//NSTimer *timer;<br />//double interval;<br />- (IBAction)timerDo:(id)sender<br />{<br />&nbsp; &nbsp; [timer invalidate];<br />&nbsp; &nbsp; timer = [NSTimer scheduledTimerWithTimeInterval:interval<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;target:self<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;selector:@selector(doTimer:)<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;userInfo:nil<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; repeats:YES];<br />}
    
  • NseaProtectorNseaProtector Membre
    00:21 modifié #15
    oui c'est pas faux, en fait à  bien y réfléchir ta raison, sauf qu'il faudra pas oublié de le détruire, enfin je crois.
  • psychoh13psychoh13 Mothership Developer Membre
    00:21 modifié #16
    il faut en effet mettre un invalidate encore quelque part.

    Mais dans cette méthode, ce code suffit.
  • NseaProtectorNseaProtector Membre
    00:21 modifié #17
    dans 1227728830:

    il faut en effet mettre un invalidate encore quelque part.

    Mais dans cette méthode, ce code suffit.

    Un invalidate quelque part ?
    Un release plutôt et j'essaierais de le mettre en
    -(void)dealloc

    avec un nslog pour voir si le dealloc est appelé en quittant
  • psychoh13psychoh13 Mothership Developer Membre
    00:21 modifié #18
    Non, un release te ferait planter...

    L'invalidate fait le release lui-même, et tu ne dois faire de release que si tu fais un retain avant, ou bien un alloc+init ou un copy. Aucun de ces 3 cas ne se rencontre ici...
    C'est d'ailleurs pour ça que je n'ai pas mis de release dans la correction que j'ai apportée.

    Le invalidate supprime le timer du runloop dans lequel il tourne, le release ne fait qu'enlever une référence. Et en prime, le invalidate fait le release nécessaire... En fait, c'est le runloop qui fait le release quand le timer se retire de la boucle d'exécution, puisqu'il n'est pas détenu par l'objet qui la créé (puisqu'on n'utilise ni retain ni alloc+init) et bien le timer est tout simplement désalloué.
  • AliGatorAliGator Membre, Modérateur
    novembre 2008 modifié #19
    Non, Nsea, pas de release dans le dealloc. Il faut bien faire un invalidate et surtout pas de release.

    D'une part, tu n'as pas fait de "alloc" ni de "copy" donc tu n'as pas à  faire de release. Règle de base de la gestion mémoire ;)

    De plus avec [tt]scheduledTimerWithTimeInterval:...[/tt], le nom indique déjà  que c'est un constructeur de commodité, donc que tu n'as pas à  en gérer le "release", mais en plus ça installe automatiquement le timer nouvellement créé dans la RunLoop courante (et c'est la RunLoop du coup qui possède le timer du coup et garde le grapin dessus jusqu'à  ce que tu l'invalides). Par contre pour le désinstaller de sur la RunLoop, il faut bien appeler "invalidate".

    Pour plus d'informations sur NSTimer, CocoaDev a une page qui aide bien à  ce sujet.


    (Bon je me suis fait griller par psychoh13 mais je poste quand même, na :P)
  • NseaProtectorNseaProtector Membre
    00:21 modifié #20
    Et bien merci, ça me permet d'en apprendre toujours plus.
  • 00:21 modifié #21
    Le code à  rajouter serait
    <br />- (void)doTimer:(NSTimer*)monTimer<br />{<br />&nbsp; &nbsp;  taratata+=1;<br />&nbsp; &nbsp;  //faire kekchose<br />&nbsp; &nbsp;  if(taratata&gt;=20)<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [monTimer invalidate];<br />}<br />
    


    Histoire que le timer tourne pas non plus pendant toute l'execution de l'application  :o à  moins que al33er en ait besoin tout le temps
  • Philippe49Philippe49 Membre
    00:21 modifié #22
    dans 1227727079:

    Ne pas oublier de déclarer les méthodes et de "Binder" le bouton a la fonction timerDo.


    Petit gris-gris de puriste ... Ne pas utiliser le verbe binder à  la place de connecter.
    binder se traduirait plutôt par synchroniser, ce qui n'est pas la même chose. Il signifierait par exemple ici que lorsqu'on appelle la méthode associée  timerDo , le bouton deviendrait highlighted.
  • NseaProtectorNseaProtector Membre
    00:21 modifié #23
    Et bien on en apprends des choses...
    J'ai mis "Binder" entre guillemet pour évoquer les Binding (liens), pour que ce soit clair. Je sais bien que les verbes anglais ne font pas leur infinitif en er, mais je vais me mettre une petite tape sur les fesses pour faire plaisir à  Philippe que j'aime beaucoup du fait de ses nombreux tutoriels.
  • Philippe49Philippe49 Membre
    00:21 modifié #24
    dans 1227787480:

    mais je vais me mettre une petite tape sur les fesses pour faire plaisir à  Philippe.

    Tape pas trop fort .. :) ou alors met un NS_Protector !  ::) 
  • al33eral33er Membre
    00:21 modifié #25
    Merci à  tous pour votre participation et de vos excellents conseils.

    J'ai mis en sortie de ma méthode qui créer le timer le timer lui même ce qui me permet de l'invalider quand je veux.

    Voilà  çà  fonctionne niquel.

    Alexandre.


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