Obtener el tiempo transcurrido en Objective-C

153

Necesito obtener el tiempo transcurrido entre dos eventos, por ejemplo, la aparición de una UIView y la primera reacción del usuario.

¿Cómo puedo lograrlo en Objective-C?

Ilya Suzdalnitski
fuente

Respuestas:

267
NSDate *start = [NSDate date];
// do stuff...
NSTimeInterval timeInterval = [start timeIntervalSinceNow];

timeInterval es la diferencia entre inicio y ahora, en segundos, con precisión de menos de milisegundos.

Can Berk Güder
fuente
18
@NicolasZozol puede usar fabs(...)para obtener el valor absoluto de un flotador. P.ej. NSTimeInterval timeInterval = fabs([start timeIntervalSinceNow]);
So Over It
1
Parece que el valor de timeInterval es negativo.
Jacky
si obtiene un valor negativo: multiplique por -1 y está bien
PinkFloydRocks
10
Confiar en él [NSDate date]puede conducir a errores difíciles de rastrear, vea esta respuesta para obtener más información.
Sentido
@PinkFloydRocks - ¿Qué? ¿Cómo se produciría legítimamente un valor negativo?
Todd Lehman
228

No debe confiar en él [NSDate date]para fines de tiempo ya que puede informar en exceso o por debajo del tiempo transcurrido. ¡Incluso hay casos en los que su computadora aparentemente viajará en el tiempo ya que el tiempo transcurrido será negativo! (Por ejemplo, si el reloj se movió hacia atrás durante el tiempo).

De acuerdo con Aria Haghighi en la conferencia "Reconocimiento avanzado de gestos iOS" del curso de invierno 2013 Stanford iOS (34:00), debe usarla CACurrentMediaTime()si necesita un intervalo de tiempo preciso.

C objetivo:

#import <QuartzCore/QuartzCore.h>
CFTimeInterval startTime = CACurrentMediaTime();
// perform some action
CFTimeInterval elapsedTime = CACurrentMediaTime() - startTime;

Rápido:

let startTime = CACurrentMediaTime()
// perform some action
let elapsedTime = CACurrentMediaTime() - startTime

La razón es que se [NSDate date]sincroniza en el servidor, por lo que podría provocar "problemas de sincronización de tiempo" que pueden conducir a errores muy difíciles de rastrear. CACurrentMediaTime(), por otro lado, es un tiempo de dispositivo que no cambia con estas sincronizaciones de red.

Deberá agregar el marco QuartzCore a la configuración de su objetivo.

Sensato
fuente
17
Por favor vota esto. Aunque creo que todos pueden estar de acuerdo en que NSDate debería ser su primera opción, esta sugerencia resolvió un problema mío. Cuando llamaba [NSDate date]dentro de un bloque de finalización dentro de un bloque de despacho, recibía el mismo tiempo "actual" que informaba [NSDate date]inmediatamente antes de que la ejecución llegara al punto en el que se crearon mis bloques. CACurrentMediaTime()resuelto este problema
n00neimp0rtant el
¿Cómo usaríamos el elapsedTime para mostrar como mm: ss?
CyberMew
@CyberMew: Ese es un problema separado. Consulte ¿Cómo divido un NSTimeInterval en año, meses, días, horas, minutos y segundos en iPhone? para algunas soluciones
Senseful
12
Tenga en cuenta que CACurrentMediaTime()deja de hacer tictac cuando el dispositivo entra en suspensión. Si prueba con el dispositivo desconectado de una computadora, bloquéelo, luego espere ~ 10 minutos y verá que CACurrentMediaTime()no cumple con la hora del reloj de pared.
russbishop
agregar e importar #import <QuartzCore / QuartzCore.h>
steveen zoleko
23

Usa el timeIntervalSinceDatemétodo

NSTimeInterval secondsElapsed = [secondDate timeIntervalSinceDate:firstDate];

NSTimeIntervales solo un double, definir NSDateasí:

typedef double NSTimeInterval;
Marco Lazzeri
fuente
15

Para cualquiera que venga aquí en busca de una implementación getTickCount () para iOS, aquí está el mío después de juntar varias fuentes.

Anteriormente tenía un error en este código (primero lo dividí por 1000000) que estaba causando cierta cuantificación de la salida en mi iPhone 6 (tal vez esto no fue un problema en el iPhone 4 / etc o simplemente nunca lo noté). Tenga en cuenta que al no realizar esa división primero, existe cierto riesgo de desbordamiento si el numerador de la base de tiempo es bastante grande. Si alguien tiene curiosidad, hay un enlace con mucha más información aquí: https://stackoverflow.com/a/23378064/588476

A la luz de esa información, ¡quizás sea más seguro usar la función de Apple CACurrentMediaTime!

También mach_timebase_infocomparé la llamada y toma aproximadamente 19ns en mi iPhone 6, así que eliminé el código (no seguro) que estaba almacenando en caché la salida de esa llamada.

#include <mach/mach.h>
#include <mach/mach_time.h>

uint64_t getTickCount(void)
{
    mach_timebase_info_data_t sTimebaseInfo;
    uint64_t machTime = mach_absolute_time();

    // Convert to milliseconds
    mach_timebase_info(&sTimebaseInfo);
    machTime *= sTimebaseInfo.numer;
    machTime /= sTimebaseInfo.denom;
    machTime /= 1000000; // convert from nanoseconds to milliseconds

    return machTime;
}

Tenga en cuenta el riesgo potencial de desbordamiento dependiendo de la salida de la llamada a la base de tiempo. Sospecho (pero no sé) que podría ser una constante para cada modelo de iPhone. en mi iPhone 6 lo fue 125/3.

La solución usando CACurrentMediaTime()es bastante trivial:

uint64_t getTickCount(void)
{
    double ret = CACurrentMediaTime();
    return ret * 1000;
}
Wayne Uroda
fuente
¿Alguien sabe por qué hay una transmisión redundante (nula) en la llamada mach_timebase_info?
Simon Tillson
@SimonTillson esa es una buena pregunta: o bien se requería silenciar una advertencia, o la copié de otro lugar y simplemente no me di cuenta de que debía eliminarla. No me acuerdo
Wayne Uroda
2
Esto no es seguro para subprocesos. mach_timebase_info()se restablecerá pasado en mach_timebase_info_data_tque {0,0}antes de que a los valores reales, que pueden causar la división por cero si el código es llamado desde varios subprocesos. Use en su dispatch_once()lugar (o CACurrentMediaTimeque es solo tiempo de máquina convertido a segundos).
wonder.mice
Gracias @ wonder.mice, he actualizado mi respuesta (también encontré un problema con respecto a la cuantización, me culpo de 6 años más joven ...)
Wayne Uroda
4

use la función timeIntervalSince1970 de la clase NSDate como se muestra a continuación:

double start = [startDate timeIntervalSince1970];
double end = [endDate timeIntervalSince1970];
double difference = end - start;

Básicamente, esto es lo que uso para comparar la diferencia en segundos entre 2 fechas diferentes. también revise este enlace aquí

Raj
fuente
0

Las otras respuestas son correctas (con una advertencia *). Agrego esta respuesta simplemente para mostrar un ejemplo de uso:

- (void)getYourAffairsInOrder
{
    NSDate* methodStart = [NSDate date];  // Capture start time.

    // … Do some work …

    NSLog(@"DEBUG Method %s ran. Elapsed: %f seconds.", __func__, -([methodStart timeIntervalSinceNow]));  // Calculate and report elapsed time.
}

En la consola del depurador, verá algo como esto:

DEBUG Method '-[XMAppDelegate getYourAffairsInOrder]' ran. Elapsed: 0.033827 seconds.

* Advertencia: como otros han mencionado, úselo NSDatepara calcular el tiempo transcurrido solo para fines casuales. Uno de esos propósitos podría ser la prueba común, la elaboración de perfiles en bruto, donde solo desea una idea aproximada de cuánto tiempo lleva un método.

El riesgo es que la configuración de la hora actual del reloj del dispositivo podría cambiar en cualquier momento debido a la sincronización del reloj de la red. Entonces el NSDatetiempo podría saltar hacia adelante o hacia atrás en cualquier momento.

Albahaca Bourque
fuente