Como uso o NSTimer?

344

Como uso um NSTimer? Alguém pode me dar instruções passo a passo?

lab12
fonte
11
eu tenho informações parameterwise em pravinmagdum.wordpress.com/2010/12/30/how-to-use-nstimer ter o olhar sobre ele .. pode ser útil para você

Respostas:

616

Em primeiro lugar, gostaria de chamar sua atenção para a documentação do Cocoa / CF (que é sempre um excelente primeiro ponto de escala). Os documentos da Apple têm uma seção na parte superior de cada artigo de referência chamada "Guias complementares", que lista os guias para o tópico que está sendo documentado (se houver algum). Por exemplo, com NSTimer, a documentação lista dois guias complementares:

Para sua situação, o artigo Tópicos de programação do timer provavelmente será o mais útil, enquanto os tópicos de encadeamento estão relacionados, mas não os mais diretamente relacionados à classe que está sendo documentada. Se você der uma olhada no artigo Tópicos de programação do timer, ele está dividido em duas partes:

  • Temporizadores
  • Usando temporizadores

Para artigos que usam esse formato, geralmente há uma visão geral da classe e para que ela é usada e, em seguida, alguns exemplos de código de como usá-la, neste caso na seção "Usando temporizadores". Existem seções em "Criando e agendando um timer", "Parando um timer" e "Gerenciamento de memória". A partir do artigo, a criação de um cronômetro agendado e sem repetição pode ser feita da seguinte maneira:

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

Isto irá criar um temporizador que é acionado após 2,0 segundos e chama targetMethod:a selfcom um argumento, que é um ponteiro para a NSTimerinstância.

Se você quiser examinar mais detalhadamente o método, consulte os documentos para obter mais informações, mas também há explicações sobre o código.

Se você deseja interromper um cronômetro que seja repetido (ou interromper um cronômetro que não se repita antes que ele seja acionado), será necessário manter um ponteiro para a NSTimerinstância que foi criada; frequentemente, isso precisará ser uma variável de instância para que você possa se referir a ela em outro método. Você pode chamar invalidatea NSTimerinstância:

[myTimer invalidate];
myTimer = nil;

Também é uma boa prática nilextrair a variável da instância (por exemplo, se o método que invalida o timer for chamado mais de uma vez e a variável da instância não tiver sido definida nile a NSTimerinstância tiver sido desalocada, será lançada uma exceção).

Observe também o ponto sobre Gerenciamento de memória na parte inferior do artigo:

Como o loop de execução mantém o cronômetro, da perspectiva do gerenciamento de memória , normalmente não há necessidade de manter uma referência a um cronômetro depois que você o agendou . Como o cronômetro é passado como argumento quando você especifica seu método como seletor, você pode invalidar um cronômetro de repetição quando apropriado dentro desse método . Em muitas situações, no entanto, você também deseja a opção de invalidar o cronômetro - talvez mesmo antes de iniciar. Nesse caso, você precisa manter uma referência ao cronômetro, para poder enviar uma mensagem inválida sempre que apropriado. Se você criar um cronômetro não programado (consulte “Temporizadores não programados”), deverá manter uma forte referência ao cronômetro (em um ambiente contado por referência, você o mantém) para que ele não seja desalocado antes de usá-lo.

Alex Rozanski
fonte
Okk, uma pergunta, o que eu colocaria como meu código que seria executado a cada dois segundos?
lab12
Você iria passar YESpara repeats:quando você chamar scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:. Se você fizer isso, mantenha uma referência à NSTimerinstância (ela é retornada pelo método) e siga o ponto em Gerenciamento de memória, conforme detalhado acima.
Alex Rozanski
Não, onde eu colocaria meu código que seria iniciado a cada 2 segundos? Por exemplo, digamos que eu quisesse emitir um sinal sonoro a cada 2 segundos. onde eu colocaria o código de bip ..?
lab12
No método que você especificar com o targete selector. Por exemplo, se o seu destino é selfe o seletor é timerMethod:, o método chamado quando o timer é acionado é timerMethod:definido no self. Você pode colocar o código que desejar nesse método, e o método será chamado sempre que o timer for acionado. Observe que o método chamado quando o timer é acionado (que você transmite como selector:) pode receber apenas um argumento (que quando chamado é um ponteiro para a NSTimerinstância).
Alex Rozanski
Desculpe, significava "definido em self"
Alex Rozanski
331

existem algumas maneiras de usar um timer:

1) cronômetro programado e usando o seletor

NSTimer *t = [NSTimer scheduledTimerWithTimeInterval: 2.0
                      target: self
                      selector:@selector(onTick:)
                      userInfo: nil repeats:NO];
  • se você definir repetições para NÃO, o temporizador aguardará 2 segundos antes de executar o seletor e depois parará;
  • se repetir: SIM, o temporizador iniciará imediatamente e repetirá a chamada do seletor a cada 2 segundos;
  • para parar o cronômetro, você chama o método -invalidate do temporizador: [t invalidar];

Como uma observação lateral, em vez de usar um timer que não se repita e chama o seletor após um intervalo especificado, você pode usar uma declaração simples como esta:

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

isso terá o mesmo efeito que o código de exemplo acima; mas se você quiser chamar o seletor pela enésima vez, use o temporizador com repetições: YES;

2) temporizador auto-agendado

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];
  • isso criará um cronômetro que será iniciado em uma data personalizada especificada por você (nesse caso, após um minuto) e se repetirá a cada segundo

3) cronômetro não programado e usando invocação

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

e depois disso, você inicia o timer manualmente sempre que precisar:

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



E, como observação, o método onTick: se parece com o seguinte:

-(void)onTick:(NSTimer *)timer {
   //do smth
}
Woofy
fonte
Ok, mas você vê que eu quiser diminuir a transparência do meu aplicativo, então eu não sei como aplicar isso com o NSTimer
lab12
24
caramba, essas pessoas hoje .. votem contra mim porque você não especificou isso desde o início e vamos escrever em vão!
Woofy
28
Você não escreveu em vão. Esta é uma boa informação!
7129 willc2
No método 2, "cronômetro auto-agendado", como posso parar o cronômetro quando quiser?
Satyam
11
@Satyamsvv, você pode parar o cronômetro invocando, digamos, outro método com: [timer invalidate]; temporizador = nulo;
Kimpoy
59

Algo assim:

NSTimer *timer;

    timer = [NSTimer scheduledTimerWithTimeInterval: 0.5
                     target: self
                     selector: @selector(handleTimer:)
                     userInfo: nil
                     repeats: YES];
ennuikiller
fonte
14
Ainda continua forte ... 2014 também!
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
fonte
De alguma forma, cria um ciclo de retenção, para que MyViewControllernunca seja desalocado.
precisa saber é o seguinte
5
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:60 target:self selector:@selector(timerCalled) userInfo:nil repeats:NO];

-(void)timerCalled
{
     NSLog(@"Timer Called");
     // Your Code
}
Mohit
fonte
onde você escreveu isso?
Mohit
11
@JigneshB esta resposta é de apenas cerca de como usar NSTimer não se trata de usá-lo no fundo
Mohit
eu escrevi no método background, isto é, - (void) applicationDidEnterBackground: (UIApplication *) application {}
Jignesh B
com modo repetido SIM
Jignesh B
A documentação da Apple é muito específica sobre a assinatura do método de retorno de chamada. O seu está errado.
Nikolai Ruhe
2

As respostas estão faltando uma hora específica do dia aqui na 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];

Obviamente, substitua "doRefresh" pelo método desejado da sua classe

tente criar o objeto de calendário uma vez e torne allUnits uma estática para obter eficiência.

adicionar um componente de uma hora funciona perfeitamente, sem necessidade de um teste à meia-noite ( link )

gjpc
fonte