Como posso obter um tempo preciso, por exemplo, em milissegundos no Objective-C?

Existe uma maneira fácil de obter um tempo com muita precisão?

Eu preciso calcular alguns atrasos entre as chamadas de método. Mais especificamente, quero calcular a velocidade de rolagem em um UIScrollView.

NSDate e timeIntervalSince* retornarão um NSTimeInterval que é um duplo com precisão de sub-milissegundos. NSTimeInterval está em segundos, mas usa o dobro para lhe dar maior precisão.

Para calcular a precisão do tempo em milissegundos, você pode fazer:

 // Get a current time for where you want to start measuring from NSDate *date = [NSDate date]; // do work... // Find elapsed time and convert to milliseconds // Use (-) modifier to conversion since receiver is earlier than now double timePassed_ms = [date timeIntervalSinceNow] * -1000.0; 

Documentação sobre timeIntervalSinceNow .

Há muitas outras maneiras de calcular esse intervalo usando o NSDate , e eu recomendaria ver a documentação da class do NSDate que é encontrada na Referência de Classe do NSDate .

mach_absolute_time() pode ser usado para obter medições precisas.

Veja http://developer.apple.com/qa/qa2004/qa1398.html

Também está disponível o CACurrentMediaTime() , que é essencialmente a mesma coisa, mas com uma interface mais fácil de usar.

Por favor, não use NSDate , CFAbsoluteTimeGetCurrent ou gettimeofday para medir o tempo decorrido. Todos dependem do relógio do sistema, que pode mudar a qualquer momento devido a diversos motivos, como synchronization de tempo da rede (NTP), atualização do relógio (acontece com frequência para ajustar o desvio), ajustes de horário de verão, salto de segundos e assim por diante.

Isso significa que, se você estiver medindo a velocidade de download ou de upload, terá picos repentinos ou quedas nos números que não se correlacionam com o que realmente aconteceu; seus testes de desempenho terão estranhos outliers incorretos; e seus timeres manuais serão acionados após durações incorretas. O tempo pode até retroceder, e você acaba com deltas negativos, e você pode acabar com recursion infinita ou código morto (sim, eu fiz os dois).

Use mach_absolute_time . Mede segundos reais desde que o kernel foi inicializado. Ele está aumentando monotonicamente (nunca voltará) e não é afetado pelas configurações de data e hora. Como é trabalhoso, aqui está um simples wrapper que lhe dá NSTimeInterval s:

 // LBClock.h @interface LBClock : NSObject + (instancetype)sharedClock; // since device boot or something. Monotonically increasing, unaffected by date and time settings - (NSTimeInterval)absoluteTime; - (NSTimeInterval)machAbsoluteToTimeInterval:(uint64_t)machAbsolute; @end // LBClock.m #include  #include  @implementation LBClock { mach_timebase_info_data_t _clock_timebase; } + (instancetype)sharedClock { static LBClock *g; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ g = [LBClock new]; }); return g; } - (id)init { if(!(self = [super init])) return nil; mach_timebase_info(&_clock_timebase); return self; } - (NSTimeInterval)machAbsoluteToTimeInterval:(uint64_t)machAbsolute { uint64_t nanos = (machAbsolute * _clock_timebase.numer) / _clock_timebase.denom; return nanos/1.0e9; } - (NSTimeInterval)absoluteTime { uint64_t machtime = mach_absolute_time(); return [self machAbsoluteToTimeInterval:machtime]; } @end 

CFAbsoluteTimeGetCurrent() retorna o tempo absoluto como um valor double , mas eu não sei qual é a precisão dele – ele só pode atualizar a cada dúzia de milissegundos, ou pode atualizar cada microssegundo, não sei.

Eu não usaria mach_absolute_time() porque ele consulta uma combinação do kernel e do processador por um tempo absoluto usando ticks (provavelmente um tempo de atividade).

O que eu usaria:

 CFAbsoluteTimeGetCurrent(); 

Esta function é otimizada para corrigir a diferença no software e hardware iOS e OSX.

Algo Geekier

O quociente de uma diferença em mach_absolute_time() e AFAbsoluteTimeGetCurrent() é sempre em torno de 24000011.154871

Aqui está um log do meu aplicativo:

Por favor, note que o resultado final é uma diferença em CFAbsoluteTimeGetCurrent()

  2012-03-19 21:46:35.609 Rest Counter[3776:707] First Time: 353900795.609040 2012-03-19 21:46:36.360 Rest Counter[3776:707] Second Time: 353900796.360177 2012-03-19 21:46:36.361 Rest Counter[3776:707] Final Result Time (difference): 0.751137 2012-03-19 21:46:36.363 Rest Counter[3776:707] Mach absolute time: 18027372 2012-03-19 21:46:36.365 Rest Counter[3776:707] Mach absolute time/final time: 24000113.153295 2012-03-19 21:46:36.367 Rest Counter[3776:707] ----------------------------------------------------- 2012-03-19 21:46:43.074 Rest Counter[3776:707] First Time: 353900803.074637 2012-03-19 21:46:43.170 Rest Counter[3776:707] Second Time: 353900803.170256 2012-03-19 21:46:43.172 Rest Counter[3776:707] Final Result Time (difference): 0.095619 2012-03-19 21:46:43.173 Rest Counter[3776:707] Mach absolute time: 2294833 2012-03-19 21:46:43.175 Rest Counter[3776:707] Mach absolute time/final time: 23999753.727777 2012-03-19 21:46:43.177 Rest Counter[3776:707] ----------------------------------------------------- 2012-03-19 21:46:46.499 Rest Counter[3776:707] First Time: 353900806.499199 2012-03-19 21:46:55.017 Rest Counter[3776:707] Second Time: 353900815.016985 2012-03-19 21:46:55.018 Rest Counter[3776:707] Final Result Time (difference): 8.517786 2012-03-19 21:46:55.020 Rest Counter[3776:707] Mach absolute time: 204426836 2012-03-19 21:46:55.022 Rest Counter[3776:707] Mach absolute time/final time: 23999996.639500 2012-03-19 21:46:55.024 Rest Counter[3776:707] ----------------------------------------------------- 
 #define CTTimeStart() NSDate * __date = [NSDate date] #define CTTimeEnd(MSG) NSLog(MSG " %g",[__date timeIntervalSinceNow]*-1) 

Uso:

 CTTimeStart(); ... CTTimeEnd(@"that was a long time:"); 

Saída:

 2013-08-23 15:34:39.558 App-Dev[21229:907] that was a long time: .0023 

Além disso, aqui está como calcular um NSNumber 64 bits inicializado com a época do Unix em milissegundos, caso seja assim que você deseja armazená-lo no CoreData. Eu precisava disso para meu aplicativo, que interage com um sistema que armazena datas dessa maneira.

  + (NSNumber*) longUnixEpoch { return [NSNumber numberWithLongLong:[[NSDate date] timeIntervalSince1970] * 1000]; } 

Eu sei que este é um antigo, mas até eu me vi passando por ele novamente, então eu pensei em enviar minha própria opção aqui.

A melhor aposta é verificar o meu post no blog sobre isso: tempo coisas em Objective-C: Um cronômetro

Basicamente, eu escrevi uma aula que deixa de assistir de uma forma muito básica, mas é encapsulada para que você só precise fazer o seguinte:

 [MMStopwatchARC start:@"My Timer"]; // your work here ... [MMStopwatchARC stop:@"My Timer"]; 

E você acaba com:

 MyApp[4090:15203] -> Stopwatch: [My Timer] runtime: [0.029] 

no log …

Mais uma vez, confira o meu post para um pouco mais ou faça o download aqui: MMStopwatch.zip

Funções baseadas em mach_absolute_time são boas para medições curtas.
Mas, para as medições longas, é importante que parem de funcionar enquanto o dispositivo está adormecido.

Existe uma function para obter tempo desde a boot. Não pára enquanto dorme. Além disso, gettimeofday não é monótono, mas em meus experimentos sempre vejo que o tempo de boot muda quando a hora do sistema muda, então acho que deveria funcionar bem.

 func timeSinceBoot() -> TimeInterval { var bootTime = timeval() var currentTime = timeval() var timeZone = timezone() let mib = UnsafeMutablePointer.allocate(capacity: 2) mib[0] = CTL_KERN mib[1] = KERN_BOOTTIME var size = MemoryLayout.size(ofValue: bootTime) var timeSinceBoot = 0.0 gettimeofday(&currentTime, &timeZone) if sysctl(mib, 2, &bootTime, &size, nil, 0) != -1 && bootTime.tv_sec != 0 { timeSinceBoot = Double(currentTime.tv_sec - bootTime.tv_sec) timeSinceBoot += Double(currentTime.tv_usec - bootTime.tv_usec) / 1000000.0 } return timeSinceBoot } 

E desde o iOS 10 e o MacOS 10.12, podemos usar o CLOCK_MONOTONIC:

 if #available(OSX 10.12, *) { var uptime = timespec() if clock_gettime(CLOCK_MONOTONIC_RAW, &uptime) == 0 { return Double(uptime.tv_sec) + Double(uptime.tv_nsec) / 1000000000.0 } } 

Resumindo:

  • Date.timeIntervalSinceReferenceDate – muda quando a hora do sistema muda, não monótona
  • CFAbsoluteTimeGetCurrent() – não monotônico, pode retroceder
  • CACurrentMediaTime() – pára de marcar quando o dispositivo está adormecido
  • timeSinceBoot() – não dorme, mas pode não ser monótono
  • CLOCK_MONOTONIC – não dorme, monotônico, suportado desde o iOS 10

Para aqueles que precisamos da versão Swift da resposta do @Jeff Thompson:

 // Get a current time for where you want to start measuring from var date = NSDate() // do work... // Find elapsed time and convert to milliseconds // Use (-) modifier to conversion since receiver is earlier than now var timePassed_ms: Double = date.timeIntervalSinceNow * -1000.0 

Espero que isso lhe ajude.

Você pode obter o tempo atual em milissegundos desde 1º de janeiro de 1970 usando um NSDate:

 - (double)currentTimeInMilliseconds { NSDate *date = [NSDate date]; return [date timeIntervalSince1970]*1000; }