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(¤tTime, &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; }