¿Cómo uso NSTimer?

Respuestas:

616

En primer lugar, me gustaría llamar su atención sobre la documentación de Cocoa / CF (que siempre es un excelente primer puerto de escala). Los documentos de Apple tienen una sección en la parte superior de cada artículo de referencia llamada "Guías complementarias", que enumera las guías para el tema que se está documentando (si existe). Por ejemplo, con NSTimer, la documentación enumera dos guías complementarias:

Para su situación, es probable que el artículo Temas de programación del temporizador sea el más útil, mientras que los temas de subprocesos están relacionados pero no los más directamente relacionados con la clase que se está documentando. Si echa un vistazo al artículo de Temas de programación del temporizador, se divide en dos partes:

  • Temporizadores
  • Usando temporizadores

Para los artículos que toman este formato, a menudo hay una descripción general de la clase y para qué se usa, y luego un código de muestra sobre cómo usarlo, en este caso en la sección "Uso de temporizadores". Hay secciones sobre "Crear y programar un temporizador", "Detener un temporizador" y "Administración de memoria". A partir del artículo, la creación de un temporizador no repetido programado se puede hacer de la siguiente manera:

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

Esto creará un temporizador que se dispara después de 2,0 segundos y llama targetMethod:sobre selfcon un argumento, que es un puntero a la NSTimerinstancia.

Si luego desea ver con más detalle el método, puede consultar los documentos para obtener más información, pero también hay una explicación sobre el código.

Si desea detener un temporizador que es uno que se repite (o detener un temporizador que no se repite antes de que se active), debe mantener un puntero a la NSTimerinstancia que se creó; a menudo esto tendrá que ser una variable de instancia para que pueda consultarlo en otro método. Luego puede llamar invalidatea la NSTimerinstancia:

[myTimer invalidate];
myTimer = nil;

También es una buena práctica nileliminar la variable de instancia (por ejemplo, si su método que invalida el temporizador se llama más de una vez y la variable de instancia no se ha establecido nily la NSTimerinstancia se ha desasignado, arrojará una excepción).

Tenga en cuenta también el punto sobre la gestión de memoria al final del artículo:

Debido a que el ciclo de ejecución mantiene el temporizador, desde la perspectiva de la administración de memoria , generalmente no es necesario mantener una referencia a un temporizador después de haberlo programado . Dado que el temporizador se pasa como argumento cuando especifica su método como selector, puede invalidar un temporizador de repetición cuando sea apropiado dentro de ese método . Sin embargo, en muchas situaciones, también desea la opción de invalidar el temporizador, tal vez incluso antes de que comience. En este caso, debe mantener una referencia al temporizador, para poder enviarle un mensaje de invalidación cuando sea apropiado. Si crea un temporizador no programado (consulte “Temporizadores no programados”), debe mantener una fuerte referencia al temporizador (en un entorno de recuento de referencia, lo conserva) para que no se desasigne antes de usarlo.

Alex Rozanski
fuente
Ok, una pregunta, ¿qué pondría como código que se ejecutaría cada dos segundos?
lab12
Pasarías YESpor repeats:cuando llames scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:. Si lo hace, asegúrese de mantener una referencia a la NSTimerinstancia (es devuelta por el método) y siga el punto de Administración de memoria como se detalla anteriormente.
Alex Rozanski
No, ¿dónde pondría mi código que se iniciaría cada 2 segundos? Por ejemplo, digamos que quería que emitiera un pitido cada 2 segundos. ¿Dónde pondría el código de ruido de pitido ...?
lab12
En el método que especifique con targety selector. Por ejemplo, si su objetivo es selfy el selector es timerMethod:, el método llamado cuando se dispara el temporizador se timerMethod:define en el self. Luego puede poner el código que desee en ese método, y se llamará al método cada vez que se active el temporizador. Tenga en cuenta que el método llamado cuando se activa el temporizador (que pasa como selector:) solo puede tomar un argumento (que cuando se llama es un puntero a la NSTimerinstancia).
Alex Rozanski
Lo sentimos, significa "definido en self"
Alex Rozanski
331

Hay un par de formas de usar un temporizador:

1) temporizador programado y selector de uso

NSTimer *t = [NSTimer scheduledTimerWithTimeInterval: 2.0
                      target: self
                      selector:@selector(onTick:)
                      userInfo: nil repeats:NO];
  • si configura las repeticiones en NO, el temporizador esperará 2 segundos antes de ejecutar el selector y luego se detendrá;
  • si repite: SI, el temporizador comenzará inmediatamente y repetirá llamando al selector cada 2 segundos;
  • para detener el temporizador, llame al método de invalidación del temporizador: [t invalidate];

Como nota al margen, en lugar de usar un temporizador que no se repite y llama al selector después de un intervalo específico, puede usar una declaración simple como esta:

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

esto tendrá el mismo efecto que el código de muestra anterior; pero si desea llamar al selector cada enésima vez, use el temporizador con repeticiones: YES;

2) temporizador auto-programado

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];
  • esto creará un temporizador que se iniciará en una fecha personalizada especificada por usted (en este caso, después de un minuto) y se repetirá cada segundo

3) temporizador no programado y uso de invocación

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];

y después de eso, inicias el temporizador manualmente cuando lo necesites así:

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



Y como nota, onTick: el método se ve así:

-(void)onTick:(NSTimer *)timer {
   //do smth
}
Woofy
fuente
Ok, pero ves que quiero reducir la transparencia de mi aplicación, así que no sé cómo aplicar eso con el NSTimer
lab12
24
Dios mío, estas personas hoy ... ¡vota por mí porque no lo especificaste desde el principio y déjanos escribir en vano!
Woofy
28
No escribiste en vano. Esta es una buena información!
willc2
En el método 2, "temporizador autoprogramado", ¿cómo puedo detener el temporizador cuando lo desee?
Satyam
1
@Satyamsvv, puede detener el temporizador invocando, digamos otro método que tenga: [invalidar temporizador]; temporizador = nulo;
Kimpoy
59

Algo como esto:

NSTimer *timer;

    timer = [NSTimer scheduledTimerWithTimeInterval: 0.5
                     target: self
                     selector: @selector(handleTimer:)
                     userInfo: nil
                     repeats: YES];
ennuikiller
fuente
14
Sigue fuerte ... ¡2014 también!
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
fuente
De alguna manera crea un ciclo de retención, por lo que MyViewControllernunca se desasigna.
Darrarski el
5
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:60 target:self selector:@selector(timerCalled) userInfo:nil repeats:NO];

-(void)timerCalled
{
     NSLog(@"Timer Called");
     // Your Code
}
Mohit
fuente
donde escribiste esto
Mohit
1
@JigneshB esta respuesta solo trata sobre cómo usar NSTimer, no sobre usarlo en segundo plano
Mohit
escribí en el método de fondo, es decir, - (vacío) applicationDidEnterBackground: (UIApplication *) application {}
Jignesh B
con modo repetido SÍ
Jignesh B
La documentación de Apple es muy específica sobre la firma del método de devolución de llamada. El tuyo está mal.
Nikolai Ruhe
2

A las respuestas les falta un temporizador específico de la hora del día aquí, en la próxima hora:

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];

Por supuesto, sustituya "doRefresh" con el método deseado de su clase

intente crear el objeto de calendario una vez y haga que allUnits sea estático para mayor eficiencia.

agregar un componente de una hora funciona bien, no es necesario realizar una prueba de medianoche ( enlace )

gjpc
fuente