Aller au contenu

NSTimer - comprendre la doc Apple


zekiller28
 Share

Messages recommandés

Hello !

 

Dans la doc Apple il est dit :

(NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)seconds invocation:(NSInvocation *)invocation repeats:(BOOL)repeats

 

J'ai donc créé une méthode comme suit :

-(void)RapportConsole
{
NSLog(@"test timer 1");
}

 

Et dans le code de mon bouton j'ai mis celà :


MyTimer=[NSTimer scheduledTimerWithTimeInterval:1 invocation:(RapportConsole) repeats:TRUE];
}


 

Sachant que j'ai fait mon pointeur de la façon suivante :

NSTimer *MyTimer;

 

Et forcément, ça marche pô :zz-big-beurk:

A priori c'est la partie "invocation" qui semble ne pas lui plaire…

J'ai merdouillé où ?

 

Le but étant que toutes les secondes un message "test timer 1" soit envoyé à ma Console.

 

D'après ce que je comprends, je devrais avoir un pointeur de type NSInvocation mais de coup comment je le fais pointer sur ma méthode "RapportConsole" ?

Lien vers le commentaire
Partager sur d’autres sites

Oublie les NSInvocation, c'est un bon aperçu de ce à quoi doit ressembler l'enfer :P

Utilise plutôt : "+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)seconds target:(id)target selector:(SEL)aSelectoruserInfo:(id)userInfo repeats:(BOOL)repeats".

Vu ton code, ça doit donner ça : "MyTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self @selector(RapportConsole) userInfo:nil repeats:YES];"

Quatre choses :

- En Objective-C, c'est YES / NO et pas TRUE / FALSE (même si ça marche aussi bien sûr).

- Un selector, c'est un "truc" (j'vais pas rentrer dans les détails) qui indique le nom du message à envoyer à target (dans ton cas, ça revient à envoyer le message "RapportConsole" à self, ce qui équivalent à [self RapportConsole]).

- La méthode à appeler par le Timer prend en général un paramètre qui est le timer lui-même (le sender qu'on retrouve assez souvent dans Cocoa). La méthode est alors plutôt "-(void)RapportConsole:(NSTimer *)timer" et pour créer le selector correspondant on fait "@selector(RapportConsole:)". C'est dans la doc aussi.

- Par convention, les noms de méthode et de variable commencent par une minuscule.

Voilà :)

Lien vers le commentaire
Partager sur d’autres sites

Par contre j'ai un souci avec la ligne

MyTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self @selector(RapportConsole) userInfo:nil repeats:YES];

Le debugger me dit qu'il attend un "[" et me propose d'un mettre un devant le "@selector" mais je n'arrive pas à savoir où mettre le crochet fermant…

 

Et pourquoi il n'y a pas de ":" après le "@selector" alors que dans la ligne d'exemple il y en a un ?

Lien vers le commentaire
Partager sur d’autres sites

pour créer le selector correspondant on fait "@selector(RapportConsole:)

j'ai essayé avec et sans les ":" après le nom de ma méthode mais ça ne fonctionne pas non plus…

Lien vers le commentaire
Partager sur d’autres sites

Bon j'avance et je corrige le code de Jipé :zz-big-bien:

 

Avec un "selector" de plus ça compile…

 MyTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(RapportConsole) userInfo:nil repeats:YES];

mais j'ai encore un souci quand je clique sur mon bouton, la Console me renvoie :

-[AppDelegate RapportConsole]: unrecognized selector sent to instance 0x102625f60

 

Bon ça compile et ça marche en laissant ma méthode comme à son origine soit :

-(void)RapportConsole

Lien vers le commentaire
Partager sur d’autres sites

Bon là, j'en ai une bien bonne :

J'ai déclaré ce pointeur :

 

int *increment=1;

 

Et dans ma méthode j'ai mis ça :

 increment=increment+1;
   NSLog(@"test timer %u",increment);

Ben vous me croirez ou pas mais dans ma console, j'ai un incrément qui se fait de 4 en 4

2013-10-18 11:48:43.558 TestTimer[46725:903] test timer 225
2013-10-18 11:48:44.558 TestTimer[46725:903] test timer 229
2013-10-18 11:48:45.558 TestTimer[46725:903] test timer 233
2013-10-18 11:48:46.558 TestTimer[46725:903] test timer 237

 

WTF ? :zz-big-beurk:

Lien vers le commentaire
Partager sur d’autres sites

Oui ! J'ai zappé le "selector:" (les arguments d'une méthode doivent tous être nommés).

Si tu fais "@selector(RapportConsole)" il faut que la méthode soit "- (void)RapportConsole".

Si tu fais "@selector(RapportConsole:)" il faut que la méthode soit "- (void)RapportConsole:(NSTimer *)timer".

On le voit peut être pas très bien dans le deuxième selector, mais le ":" est important : il indique la présence d'un premier argument à la méthode. Les arguments font partie du prototype de la méthode, donc sans argument et avec un argument on change de méthode.

Ce système de selector est dû à une des caractéristiques d'Objective-C : c'est un langage très dynamique. Tu peux créer des classes, les échanger, les modifier (rajouter / enlever des méthodes - etc.).

Pour gérer ce dynamisme, quand on appelle une méthode (qu'on l'exécute), on dit qu'on envoie un message (qui est un selector) à un objet : à une classe (NSTimer ou autre) ou à une instance de classe (MyTimer, self ou autre). Rien ne te garantit que cette classe répond à cet objet. Le compilateur te met éventuellement un warning quand il lui semble quand l'état du code au moment de la compilation, l'objet ne possède pas la méthode en question et donc ne pourra pas y répondre - c'est un warning et pas une erreur parce que rien n'empêche d'ajouter la méthode en question au moment de l'exécution. Avec ARC ça devient plus strict, mais l'idée est quand même là.

Quand tu envoies un message du genre "[monObjet maMethode]" en fait (grosso modo) le compilo génère du code pour envoyer @selector(maMethode) à l'instance "monObjet" (les NSInvocation sont une abstraction autour de tout ça).

L'erreur "unrecognized selector sent to instance" ça veut juste dire que tu as essayé d'envoyer un selector à une instance de classe, mais que cette classe ne possède pas cette méthode au moment de cet envoi, et donc ne sais pas quoi faire de ton message. Ici c'est NSTimer qui a essayé d'envoyer "RapportConsole" à une instance de "AppDelegate" à l'adresse "0x102625f60" (c'est-à-dire le target, c'est à dire self) mais cette instance ne savait pas y répondre.

Pour ce qui est du userInfo, deux choses :

- Un type "id" c'est un type qui peut désigner n'importe quelle classe Objective-C (un espèce de "void *" en C, ou "variant" en RB - quoi que ça n'est pas tout à fait pareil parce que de souvenir les variant peuvent stocker aussi les entiers ou ce genre de truc).

- Les UserInfo sont utiles quand on veut passer une information au callback (ici "RapportConsole"), sans utiliser de variable globale ou d'instance - ça permet une meilleure "atomicité" dans certains cas.

Pour le récupérer, tu peux faire "[monTimer userInfo]" (sur le timer passé en paramètre de ta méthode quand tu mets un paramètre ou sur "MyTimer" - ils pointent de toute façon le même timer).

Lien vers le commentaire
Partager sur d’autres sites

Bon j'ai trouvé…

C'est encore une histoire de pointeur mais j'aimerai bien comprendre… Il suffisait de mettre :

"int increment=1;"

Pour que l'incrément se fasse de 1 en 1 (en commençant à 2 ce qui est logique puisqu'à mon premier appel "increment" va s'incrémenter de 1 et comme la variable "increment" commence à 1).

 

Tu as vu ? je m'améliore hein ? :zz-big-blink:

 

Merci pour l'explication du userinfo.

Lien vers le commentaire
Partager sur d’autres sites

Pour ton problème de "increment" : enlève le "*" devant. Mettre une étoile, ça veut dire déclarer un pointeur. Ce n’est pas nécessaire pour un entier et c'est surtout mauvais dans ce cas là :P (je suis étonné que le compilateur n'ait pas râlé d'ailleurs ^^).

 

En gros :

- "int *increment=1;" tu lui dis de déclarer un pointeur de type "int" qui va pointer un entier qui se trouve à l'adresse "1" en mémoire RAM. Autant te dire qu'à cette adresse tu ne vas pas trouver grand-chose (en fait, ça se trouve dans ce qu'on appelle la "Page Zero" - ce n’est pas très intéressant, c'est sensé contenir des zéros et c'est sensé planter quand essaye d'y lire ou écrire du contenu - c'est fait exprès).

- "increment=increment+1;" tu lui dis d'incrémenter l’adresse mémoire sur laquelle le pointeur pointe. Sauf que quand tu fais de l'arithmétique sur un pointeur, c'est toujours pondéré par la taille de la donnée de ce qu'il pointe (ici "int"). Or je te le donne en mille, la taille d'un int dans ton cas c'est 4 octets. Donc faire "increment=increment+1" c'est incrémenter de 4 la valeur de l'adresse que le pointeur pointe.

- "NSLog(@"test timer %u",increment);" tu lui dis de t'afficher l'adresse actuelle vers laquelle le pointeur pointe. D'où tu vois une adresse qui saute de 4 en 4.

 

Si ton application ne crash pas ici, c'est uniquement parce que tu n'accède jamais au contenus pointé par ton pointeur. Tu te contente d'utiliser ton pointeur comme un entier.

Si tu avais essayé d'accéder au contenus (en lecture ou écriture), ton application aurait très probablement crashé.

 

Pour préciser mon propos de l'autre fois sur les pointeurs : un pointeur n'est en fait qu'en "entier" en mémoire qui contient une adresse mémoire. À cette adresse mémoire, on trouve la donnée pointée.

NSLog est stupide, quand tu lui dis "affiche-moi un entier avec %u" et utilise la variable 'increment' pour ça), il ne se pose pas de questions : il prend la valeur du pointer (c'est-à-dire l'adresse de la donnée pointée) et te l'affiche. Bon normalement Xcode est quand même sensé t'afficher un warning dans ce cas, car il doit deviner que ce que tu fais est étrange - même si puissance du C oblige, c'est tout à fait possible.

Lien vers le commentaire
Partager sur d’autres sites

Heu, j'ai pas dû bien comprendre ce qu'était un "int" en fait… Je pensais que c'était comme les integer de RB… Genre faire

int *mavariable

était équivalent à faire en RB :

dim mavariable as integer

 

Non ?

 

J'aurais dû passer par un NSNumber ?

 

c'est sensé contenir des zéros et c'est sensé planter quand essaye d'y lire ou écrire du contenu - c'est fait exprès).

 

Raaa les vaches ! :zz-big-nerveux:

Lien vers le commentaire
Partager sur d’autres sites

Alors "int" est bien un entier comme dans "dim mavariable as integer". Mais "int *" est un pointeur sur un entier.

 

C'est tout à fait ce qu'il fallait utiliser dans ton cas. NSNumber c'est plus une classe qui permet d'encapsuler des entiers et des booléens pour les mettre dans des conteneurs (genre NSArray ou NSDictionary). Ils ne sont pas tellement faits pour travailler dessus.

 

Si tu ne comprends pas trop les pointeurs, alors simplifie-toi le raisonnement pour l'instant, et retiens juste :

- Si j'utilise un int, un float, un double, un long, un BOOL : je ne mets pas de *

- Si j'utilise un NSSize, NSRect, NSRange : je ne mets pas de *

- Si j'utilise une classe Objective-C : je mets un *

 

Après tous les détails techniques hein... :P

Lien vers le commentaire
Partager sur d’autres sites

Ok je vais commencer par ça…

 

Un pas à la fois me suffit comme disait l'autre :zz-big-back:

 

En tout cas je progresse (un peu) sur la compréhension de la doc… J'ai réussi à stopper mon Timer et à le relancer à l'aide de boutons ad hoc et sans demander de l'aide (après savoir si c'est bien fait, c'est une autre histoire mais je pense que oui étant donné le peu de code à utiliser)… :zz-big-punk:

Lien vers le commentaire
Partager sur d’autres sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Invité
Répondre à ce sujet…

×   Collé en tant que texte enrichi.   Coller en tant que texte brut à la place

  Seulement 75 émoticônes maximum sont autorisées.

×   Votre lien a été automatiquement intégré.   Afficher plutôt comme un lien

×   Votre contenu précédent a été rétabli.   Vider l’éditeur

×   Vous ne pouvez pas directement coller des images. Envoyez-les depuis votre ordinateur ou insérez-les depuis une URL.

Chargement
 Share

×
×
  • Créer...