Comment utiliser NSTimer?

344

Comment utiliser un NSTimer? Quelqu'un peut-il me donner des instructions étape par étape?

lab12
la source
1
j'ai obtenu des informations sur les paramètres sur pravinmagdum.wordpress.com/2010/12/30/how-to-use-nstimer y avoir regardé .. peut être utile pour vous

Réponses:

616

Tout d'abord, je voudrais attirer votre attention sur la documentation Cocoa / CF (qui est toujours un excellent premier port d'escale). Les documents Apple ont une section en haut de chaque article de référence appelée "Guides d'accompagnement", qui répertorie les guides pour le sujet en cours de documentation (le cas échéant). Par exemple, avec NSTimer, la documentation répertorie deux guides complémentaires:

Pour votre situation, l'article Timer Programming Topics est probablement le plus utile, tandis que les sujets de threading sont liés, mais pas les plus directement liés à la classe en cours de documentation. Si vous jetez un œil à l'article sur les rubriques de programmation du minuteur, il est divisé en deux parties:

  • Minuteries
  • Utilisation de minuteries

Pour les articles qui prennent ce format, il y a souvent un aperçu de la classe et de son utilisation, puis un exemple de code sur la façon de l'utiliser, dans ce cas dans la section "Utilisation des minuteurs". Il y a des sections sur "Créer et planifier une minuterie", "Arrêter une minuterie" et "Gestion de la mémoire". À partir de l'article, la création d'une minuterie non répétitive planifiée peut être effectuée comme suit:

[NSTimer scheduledTimerWithTimeInterval:2.0
    target:self
    selector:@selector(targetMethod:)
    userInfo:nil
    repeats:NO];

Cela va créer une minuterie qui est déclenché après 2,0 secondes et appelle targetMethod:à selfavec un argument, qui est un pointeur vers l' NSTimerinstance.

Si vous souhaitez ensuite regarder plus en détail la méthode, vous pouvez vous référer aux documents pour plus d'informations, mais il y a aussi des explications autour du code.

Si vous souhaitez arrêter un minuteur qui se répète (ou arrêter un minuteur non répétitif avant qu'il ne se déclenche), vous devez conserver un pointeur sur l' NSTimerinstance qui a été créée; souvent, cela devra être une variable d'instance afin que vous puissiez vous y référer dans une autre méthode. Vous pouvez ensuite appeler invalidatel' NSTimerinstance:

[myTimer invalidate];
myTimer = nil;

Il est également recommandé de nilsupprimer la variable d'instance (par exemple, si votre méthode qui invalide le temporisateur est appelée plusieurs fois et que la variable d'instance n'a pas été définie sur nilet que l' NSTimerinstance a été désallouée, elle lèvera une exception).

Notez également le point sur la gestion de la mémoire au bas de l'article:

Étant donné que la boucle d'exécution maintient le minuteur, du point de vue de la gestion de la mémoire, il n'est généralement pas nécessaire de conserver une référence à un minuteur après l'avoir planifié . Étant donné que le minuteur est passé en argument lorsque vous spécifiez sa méthode en tant que sélecteur, vous pouvez invalider un minuteur répétitif lorsque cela est approprié dans cette méthode . Dans de nombreuses situations, cependant, vous souhaitez également l'option d'invalider le minuteur, peut-être même avant qu'il ne démarre. Dans ce cas, vous devez conserver une référence à la minuterie, afin de pouvoir lui envoyer un message d'invalidation chaque fois que cela est approprié.. Si vous créez un minuteur non planifié (voir «Minuteurs non planifiés»), vous devez conserver une référence forte au minuteur (dans un environnement compté par référence, vous le conservez) afin qu'il ne soit pas désalloué avant de l'utiliser.

Alex Rozanski
la source
Okk, une question, que devrais-je mettre comme mon code qui serait exécuté toutes les deux secondes?
lab12
Tu passerais YESpour repeats:quand tu appelles scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:. Si vous le faites, assurez-vous de conserver une référence à l' NSTimerinstance (elle est renvoyée par la méthode) et suivez le point sur la gestion de la mémoire comme je l'ai détaillé ci-dessus.
Alex Rozanski
Non, où mettrais-je mon code qui serait démarré toutes les 2 secondes? Par exemple, disons que je voulais qu'il émette un bip toutes les 2 secondes. où dois-je mettre le code de bruit de bip ..?
lab12
Dans la méthode que vous spécifiez avec le targetet selector. Par exemple, si votre cible est selfet que le sélecteur l'est timerMethod:, la méthode appelée lorsque le minuteur se déclenche est timerMethod:définie sur le self. Vous pouvez ensuite mettre le code que vous voulez dans cette méthode, et la méthode sera appelée chaque fois que le minuteur se déclenchera. Notez que la méthode appelée lorsque le minuteur se déclenche (que vous passez en tant que selector:) ne peut prendre qu'un seul argument (qui, lorsqu'il est appelé, est un pointeur vers l' NSTimerinstance).
Alex Rozanski
Désolé, signifiait "défini sur self"
Alex Rozanski
331

il existe deux façons d'utiliser une minuterie:

1) minuterie programmée et utilisation du sélecteur

NSTimer *t = [NSTimer scheduledTimerWithTimeInterval: 2.0
                      target: self
                      selector:@selector(onTick:)
                      userInfo: nil repeats:NO];
  • si vous définissez les répétitions sur NO, la minuterie attendra 2 secondes avant d'exécuter le sélecteur et après cela, elle s'arrêtera;
  • si répéter: OUI, la minuterie démarrera immédiatement et répétera l'appel du sélecteur toutes les 2 secondes;
  • pour arrêter le minuteur, vous appelez la méthode -invalidate du minuteur: [t invalidate];

En guise de remarque, au lieu d'utiliser une minuterie qui ne se répète pas et appelle le sélecteur après un intervalle spécifié, vous pouvez utiliser une instruction simple comme celle-ci:

[self performSelector:@selector(onTick:) withObject:nil afterDelay:2.0];

cela aura le même effet que l'exemple de code ci-dessus; mais si vous voulez appeler le sélecteur une nième fois, vous utilisez la minuterie avec des répétitions: OUI;

2) minuterie auto-programmée

NSDate *d = [NSDate dateWithTimeIntervalSinceNow: 60.0];
NSTimer *t = [[NSTimer alloc] initWithFireDate: d
                              interval: 1
                              target: self
                              selector:@selector(onTick:)
                              userInfo:nil repeats:YES];

NSRunLoop *runner = [NSRunLoop currentRunLoop];
[runner addTimer:t forMode: NSDefaultRunLoopMode];
[t release];
  • cela créera une minuterie qui démarrera à une date personnalisée spécifiée par vous (dans ce cas, après une minute), et se répétera toutes les secondes

3) Minuterie non planifiée et utilisation de l'invocation

NSMethodSignature *sgn = [self methodSignatureForSelector:@selector(onTick:)];
NSInvocation *inv = [NSInvocation invocationWithMethodSignature: sgn];
[inv setTarget: self];
[inv setSelector:@selector(onTick:)];

NSTimer *t = [NSTimer timerWithTimeInterval: 1.0
                      invocation:inv 
                      repeats:YES];

et après cela, vous démarrez le minuteur manuellement chaque fois que vous en avez besoin comme ceci:

NSRunLoop *runner = [NSRunLoop currentRunLoop];
[runner addTimer: t forMode: NSDefaultRunLoopMode];



Et comme note, la méthode onTick: ressemble à ceci:

-(void)onTick:(NSTimer *)timer {
   //do smth
}
Woofy
la source
Ok, mais vous voyez, je veux réduire la transparence de mon application, donc je ne sais pas comment l'appliquer avec le NSTimer
lab12
24
jeez, ces gens aujourd'hui .. votez contre moi parce que vous ne l'avez pas spécifié depuis le début et laissez-nous écrire en vain!
Woofy
28
Vous n'avez pas écrit en vain. Ce sont de BONNES informations!
willc2
Dans la méthode 2, "minuterie auto-programmée", comment puis-je arrêter la minuterie quand je veux?
Satyam
1
@Satyamsvv, vous pouvez arrêter le minuteur en invoquant, par exemple, une autre méthode ayant: [timer invalidate]; timer = nil;
Kimpoy
59

Quelque chose comme ça:

NSTimer *timer;

    timer = [NSTimer scheduledTimerWithTimeInterval: 0.5
                     target: self
                     selector: @selector(handleTimer:)
                     userInfo: nil
                     repeats: YES];
ennuikiller
la source
14
Toujours aussi fort ... 2014 aussi!
Muruganandham K
5
#import "MyViewController.h"

@interface MyViewController ()

@property (strong, nonatomic) NSTimer *timer;

@end

@implementation MyViewController

double timerInterval = 1.0f;

- (NSTimer *) timer {
    if (!_timer) {
        _timer = [NSTimer timerWithTimeInterval:timerInterval target:self selector:@selector(onTick:) userInfo:nil repeats:YES];
    }
    return _timer;
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    [[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}

-(void)onTick:(NSTimer*)timer
{
    NSLog(@"Tick...");
}

@end
Dan
la source
D'une manière ou d'une autre, cela crée un cycle de rétention, de sorte que le MyViewControllerjamais n'est désalloué.
Darrarski
5
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:60 target:self selector:@selector(timerCalled) userInfo:nil repeats:NO];

-(void)timerCalled
{
     NSLog(@"Timer Called");
     // Your Code
}
Mohit
la source
où avez-vous écrit cela?
Mohit
1
@JigneshB cette réponse ne concerne que la façon d'utiliser NSTimer et non de l'utiliser en arrière
Mohit
j'ai écrit dans la méthode d'arrière-plan, c'est-à-dire - (void) applicationDidEnterBackground: (UIApplication *) application {}
Jignesh B
avec mode répété OUI
Jignesh B
La documentation d'Apple est très spécifique sur la signature de la méthode de rappel. La tienne a tort.
Nikolai Ruhe
2

Il manque une réponse à une heure précise de la journée, voici l'heure suivante:

NSCalendarUnit allUnits = NSCalendarUnitYear   | NSCalendarUnitMonth |
                          NSCalendarUnitDay    | NSCalendarUnitHour  |
                          NSCalendarUnitMinute | NSCalendarUnitSecond;

NSCalendar *calendar = [[ NSCalendar alloc]  
                          initWithCalendarIdentifier:NSGregorianCalendar];

NSDateComponents *weekdayComponents = [calendar components: allUnits 
                                                  fromDate: [ NSDate date ] ];

[ weekdayComponents setHour: weekdayComponents.hour + 1 ];
[ weekdayComponents setMinute: 0 ];
[ weekdayComponents setSecond: 0 ];

NSDate *nextTime = [ calendar dateFromComponents: weekdayComponents ];

refreshTimer = [[ NSTimer alloc ] initWithFireDate: nextTime
                                          interval: 0.0
                                            target: self
                                          selector: @selector( doRefresh )
                                          userInfo: nil repeats: NO ];

[[NSRunLoop currentRunLoop] addTimer: refreshTimer forMode: NSDefaultRunLoopMode];

Bien sûr, remplacez "doRefresh" par la méthode souhaitée par votre classe

essayez de créer l'objet calendrier une fois et de rendre les allUnits statiques pour plus d'efficacité.

l'ajout d'un composant à l'heure fonctionne très bien, pas besoin de test à minuit ( lien )

gjpc
la source