¿Existe una manera fácil de obtener una hora con mucha precisión?
Necesito calcular algunos retrasos entre las llamadas al método. Más específicamente, quiero calcular la velocidad de desplazamiento en un UIScrollView.
¿Existe una manera fácil de obtener una hora con mucha precisión?
Necesito calcular algunos retrasos entre las llamadas al método. Más específicamente, quiero calcular la velocidad de desplazamiento en un UIScrollView.
Respuestas:
NSDate
y los timeIntervalSince*
métodos devolverán un NSTimeInterval
que es un doble con una precisión de menos de milisegundos. NSTimeInterval
está en segundos, pero usa el doble para darle mayor precisión.
Para calcular la precisión de tiempo de milisegundos, puede hacer:
// 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;
Documentación en timeIntervalSinceNow .
Hay muchas otras formas de calcular este intervalo utilizando NSDate
, y recomendaría mirar la documentación de la clase NSDate
que se encuentra en NSDate Class Reference .
NSDate
y mach_absolute_time()
en un nivel de alrededor de 30 ms. 27 vs. 29, 36 vs. 39, 43 vs. 45. NSDate
fue más fácil de usar para mí y los resultados fueron lo suficientemente similares como para no importarme.
mach_absolute_time()
se puede utilizar para obtener medidas precisas.
Ver http://developer.apple.com/qa/qa2004/qa1398.html
También está disponible CACurrentMediaTime()
, que es esencialmente lo mismo pero con una interfaz más fácil de usar.
(Nota: esta respuesta se escribió en 2009. Consulte la respuesta de Pavel Alexeev para conocer las clock_gettime()
interfaces POSIX más simples disponibles en las versiones más recientes de macOS e iOS).
CACurrentMediaTime()
, que se convierte mach_absolute_time()
directamente en un double
.
elapsedNano
es de tipo Nanoseconds
, que no es un tipo entero simple. Es un alias de UnsignedWide
, que es una estructura con dos campos enteros de 32 bits. Puede usar en UnsignedWideToUInt64()
lugar del yeso si lo prefiere.
No utilice NSDate
, CFAbsoluteTimeGetCurrent
ni gettimeofday
para medir el tiempo transcurrido. Todo esto depende del reloj del sistema, que puede cambiar en cualquier momento debido a muchas razones diferentes, como la sincronización de la hora de la red (NTP), la actualización del reloj (sucede a menudo para ajustar la desviación), los ajustes de DST, los segundos intercalares, etc.
Esto significa que si está midiendo su velocidad de descarga o carga, obtendrá picos o caídas repentinas en sus números que no se correlacionan con lo que realmente sucedió; sus pruebas de rendimiento tendrán valores atípicos incorrectos extraños; y sus temporizadores manuales se activarán después de duraciones incorrectas. El tiempo puede incluso retroceder y terminar con deltas negativos, y puede terminar con una recursividad infinita o un código muerto (sí, he hecho ambos).
Utilice mach_absolute_time
. Mide los segundos reales desde que se inició el kernel. Está aumentando monótonamente (nunca retrocederá) y no se ve afectado por los ajustes de fecha y hora. Dado que es difícil trabajar con él, aquí hay un envoltorio simple que le brinda 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 <mach/mach.h>
#include <mach/mach_time.h>
@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
CACurrentMediaTime()
con QuartzCore?
@import Darwin;
lugar de#include <mach/mach_time.h>
CFAbsoluteTimeGetCurrent()
devuelve el tiempo absoluto como double
valor, pero no sé cuál es su precisión: puede que solo se actualice cada docena de milisegundos, o podría actualizar cada microsegundo, no lo sé.
72.89674947369
segundos sería bastante poco común, considerando todos los demás valores que podría ser. ;)
NO lo usaría mach_absolute_time()
porque consulta una combinación del kernel y el procesador durante un tiempo absoluto usando ticks (probablemente un tiempo de actividad).
Qué usaría:
CFAbsoluteTimeGetCurrent();
Esta función está optimizada para corregir la diferencia en el software y hardware iOS y OSX.
Algo más geek
El cociente de una diferencia en mach_absolute_time()
y AFAbsoluteTimeGetCurrent()
siempre está alrededor de 24000011.154871
Aquí hay un registro de mi aplicación:
Por favor nota que el tiempo resultado final es una diferencia en CFAbsoluteTimeGetCurrent()
's
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] -----------------------------------------------------
mach_absolute_time()
con a mach_timebase_info_data
y luego lo hice (long double)((mach_absolute_time()*time_base.numer)/((1000*1000)*time_base.denom));
. Para obtener la base de tiempo de Mach, puede hacerlo(void)mach_timebase_info(&your_timebase);
#define CTTimeStart() NSDate * __date = [NSDate date]
#define CTTimeEnd(MSG) NSLog(MSG " %g",[__date timeIntervalSinceNow]*-1)
Uso:
CTTimeStart();
...
CTTimeEnd(@"that was a long time:");
Salida:
2013-08-23 15:34:39.558 App-Dev[21229:907] that was a long time: .0023
NSDate
depende del reloj del sistema, que puede modificarse en cualquier momento, lo que puede dar lugar a intervalos de tiempo incorrectos o incluso negativos. Úselo en su mach_absolute_time
lugar para obtener el tiempo transcurrido correcto.
mach_absolute_time
puede verse afectado por los reinicios del dispositivo. En su lugar, utilice la hora del servidor.
Además, aquí se explica cómo calcular un 64 bits NSNumber
inicializado con la época de Unix en milisegundos, en caso de que así sea como desee almacenarlo en CoreData. Necesitaba esto para mi aplicación que interactúa con un sistema que almacena fechas de esta manera.
+ (NSNumber*) longUnixEpoch {
return [NSNumber numberWithLongLong:[[NSDate date] timeIntervalSince1970] * 1000];
}
Las funciones basadas en mach_absolute_time
son buenas para mediciones cortas.
Pero para mediciones largas, una advertencia importante es que dejan de hacer tictac mientras el dispositivo está inactivo.
Hay una función para obtener el tiempo desde el inicio. No se detiene mientras duerme. Además, gettimeofday
no es monótono, pero en mis experimentos siempre he visto que el tiempo de arranque cambia cuando cambia el tiempo del sistema, así que creo que debería funcionar bien.
func timeSinceBoot() -> TimeInterval
{
var bootTime = timeval()
var currentTime = timeval()
var timeZone = timezone()
let mib = UnsafeMutablePointer<Int32>.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
}
Y desde iOS 10 y macOS 10.12 podemos usar 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
}
}
Para resumirlo:
Date.timeIntervalSinceReferenceDate
- cambia cuando cambia la hora del sistema, no monótonoCFAbsoluteTimeGetCurrent()
- no monótono, puede retrocederCACurrentMediaTime()
- deja de hacer tictac cuando el dispositivo está dormidotimeSinceBoot()
- no duerme, pero puede que no sea monótonoCLOCK_MONOTONIC
- no duerme, monótono, compatible desde iOS 10Sé que este es antiguo, pero incluso yo me encontré vagando de nuevo, así que pensé en enviar mi propia opción aquí.
La mejor opción es consultar mi publicación de blog sobre esto: cronometraje en Objective-C: un cronómetro
Básicamente, escribí una clase que deja de mirar de una manera muy básica, pero está encapsulada, por lo que solo necesita hacer lo siguiente:
[MMStopwatchARC start:@"My Timer"];
// your work here ...
[MMStopwatchARC stop:@"My Timer"];
Y terminas con:
MyApp[4090:15203] -> Stopwatch: [My Timer] runtime: [0.029]
en el registro ...
Una vez más, consulte mi publicación para obtener más información o descárguela aquí: MMStopwatch.zip
NSDate
depende del reloj del sistema, que puede modificarse en cualquier momento, lo que puede dar lugar a intervalos de tiempo incorrectos o incluso negativos. Úselo en su mach_absolute_time
lugar para obtener el tiempo transcurrido correcto.
Puede obtener la hora actual en milisegundos desde el 1 de enero de 1970 utilizando un NSDate:
- (double)currentTimeInMilliseconds {
NSDate *date = [NSDate date];
return [date timeIntervalSince1970]*1000;
}
Para aquellos, necesitamos la versión Swift de la respuesta de @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 esto te ayude.
NSDate
depende del reloj del sistema, que puede modificarse en cualquier momento, lo que puede dar lugar a intervalos de tiempo incorrectos o incluso negativos. Úselo en su mach_absolute_time
lugar para obtener el tiempo transcurrido correcto.