NSTimer não triggers quando o runloop está bloqueado

Acabei de terminar com o meu aplicativo e o teste beta encontrou um bug na parte do cronômetro … O cronômetro usa um nstimer para fazer a contagem e tem uma tabela para armazenar voltas, mas quando a mesa de voltas é rolada, o relógio para ou pausa e não compensa o tempo perdido.

Isso foi stalling foi eliminado usando:

startingTime = [[NSDate date] timeIntervalSince1970]; 

para calcular o tempo decorrido.

mas eu ainda estou usando o NSTimer para acionar a cada 0,1 segundos e isso significa que a rolagem ainda impede o timer, mesmo que o tempo decorrido será atualizado corretamente no final … e comparando isso com o cronômetro da Apple, isso me faz pensar se o cronômetro tem um thread separado apenas para a contagem do tempo decorrido. Alguém sabe se é assim que é feito?

Agora, usar o tempo desde a Época está funcionando bem em um sentido, mas complica a questão de iniciar, parar e reiniciar o cronômetro

quando o relógio é parado, o tempo é armazenado e usado para calcular um deslocamento para quando o relógio é reiniciado, mas parece haver alguma latência introduzida e o tempo salta à frente visivelmente quando o relógio é reiniciado.

Qualquer pensamento em relação à causa raiz ou a uma solução seria muito apreciado.

A resposta do bbum fornece uma maneira melhor de projetar seu aplicativo, mas se você quiser que seu timer seja acionado, independentemente de o usuário estar manipulando a UI ou não, você precisará adicioná-la ao modo de rastreamento do runloop.

Supondo que você esteja desenvolvendo para o iPhone, esse modo é UITrackingRunLoopMode . Se você está desenvolvendo para o Mac, existe um NSEventTrackingRunLoopMode com o mesmo nome.

 NSRunLoop *runloop = [NSRunLoop currentRunLoop]; NSTimer *timer = [NSTimer timerWithTimeInterval:0.1 target:self selector:@selector(myTimerAction:) userInfo:nil repeats:YES]; [runloop addTimer:timer forMode:NSRunLoopCommonModes]; [runloop addTimer:timer forMode:UITrackingRunLoopMode]; 

Se o loop de events não estiver em execução, todos os timers não serão triggersdos até que o loop de events possa ser executado novamente. Mesmo que o loop de events não seja bloqueado, não é garantido que o timer seja triggersdo exatamente no intervalo configurado. Se os seus horários foram baseados inteiramente em triggersdores de timers, a quantidade de erros aumentará com o tempo.

Você precisa acompanhar a duração separadamente do disparo dos timers. Cada vez que um timer triggers, recalcule sua duração e volte a exibir.

Para um tipo de configuração iniciar / pausar / reiniciar / parar, você geralmente deseja:

  • pegue o tempo após o início (como uma instância NSDate ou como um valor NSTimeInterval)

  • após a pausa ou parar, agarre o tempo após a pausa / parada. Subtraia a hora de início a partir desta hora e você terá a duração do intervalo

  • ao reiniciar, agarre o tempo após a reboot, mas também mantenha a duração já decorrida

  • ao pausar / parar, agarre a hora em pausa / parada e adicione a duração já decorrida

Em geral, fazer tudo isso com valores NSTimeInterval – que são apenas duplos – é mais fácil. No entanto, se você precisar acompanhar o momento real no momento em que os events aconteceram, use as instâncias do NSDate.