Ottenere il tempo trascorso in Objective-C


153

Devo passare il tempo tra due eventi, ad esempio l'aspetto di un UIView e la prima reazione dell'utente.

Come posso raggiungerlo in Objective-C?

Risposte:


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

timeInterval è la differenza tra inizio e ora, in secondi, con precisione inferiore al millisecondo.


18
@NicolasZozol è possibile utilizzare fabs(...)per ottenere il valore assoluto di un float. Per esempio. NSTimeInterval timeInterval = fabs([start timeIntervalSinceNow]);
So Over It il

1
sembra che il valore di timeInterval sia negativo.
Jacky,

se ottieni un valore negativo - moltiplica per -1 e stai bene
PinkFloydRocks

10
Affidarsi a ciò [NSDate date]potrebbe portare a rintracciare bug difficili, vedere questa risposta per maggiori informazioni.
Senso

@PinkFloydRocks - Cosa? Come potrebbe mai verificarsi un valore negativo?
Todd Lehman,

228

Non fare affidamento a [NSDate date]fini di temporizzazione poiché può sovrastimare o sottostimare il tempo trascorso. Ci sono anche casi in cui il tuo computer apparentemente viaggerà nel tempo poiché il tempo trascorso sarà negativo! (Ad esempio se l'orologio si spostava indietro durante il cronometraggio.)

Secondo Aria Haghighi nella conferenza "Advanced iOS Gesture Recognition" del corso iOS 2013 di Winter 2013 di Stanford (34:00), dovresti usare CACurrentMediaTime()se hai bisogno di un intervallo di tempo preciso.

Objective-C:

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

Swift:

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

Il motivo è che si [NSDate date]sincronizza sul server, quindi potrebbe portare a "singhiozzi di sincronizzazione temporale" che possono portare a bug molto difficili da rintracciare. CACurrentMediaTime(), d'altra parte, è un tempo del dispositivo che non cambia con queste sincronizzazioni di rete.

Dovrai aggiungere il framework QuartzCore alle impostazioni del tuo target.


17
Per favore, vota questo. Anche se penso che tutti possano essere d'accordo sul fatto che NSDate dovrebbe essere la tua prima opzione, questo suggerimento ha risolto un mio problema. Quando chiamavo [NSDate date]all'interno di un blocco di completamento all'interno di un blocco di invio, stavo ricevendo lo stesso tempo "attuale" che era stato segnalato [NSDate date]immediatamente prima che l'esecuzione raggiungesse il punto in cui i miei blocchi venivano creati. CACurrentMediaTime()risolto questo problema.
n00neimp0rtant,

Come utilizzeremmo il tempo trascorso per la visualizzazione come mm: ss?
CyberMew,

@CyberMew: questo è un problema separato. Vedi Come posso suddividere un NSTimeInterval in anno, mesi, giorni, ore, minuti e secondi su iPhone? per alcune soluzioni.
Senso

12
Ricorda che CACurrentMediaTime()smette di spuntare quando il dispositivo entra in modalità di sospensione. Se esegui il test con il dispositivo disconnesso da un computer, bloccalo, quindi attendi ~ 10 minuti e scoprirai che CACurrentMediaTime()non è al passo con il tempo dell'orologio a muro.
vescovo russo,

aggiungi e importa #import <QuartzCore / QuartzCore.h>
steveen zoleko,

23

Usa il timeIntervalSinceDatemetodo

NSTimeInterval secondsElapsed = [secondDate timeIntervalSinceDate:firstDate];

NSTimeIntervalè solo un double, definire in NSDatequesto modo:

typedef double NSTimeInterval;

15

Per chiunque venga qui alla ricerca di un'implementazione getTickCount () per iOS, ecco il mio dopo aver messo insieme varie fonti.

In precedenza avevo un bug in questo codice (prima l'ho diviso per 1000000) che causava una certa quantizzazione dell'output sul mio iPhone 6 (forse questo non era un problema su iPhone 4 / etc o non l'avevo mai notato). Notare che non eseguendo prima quella divisione, c'è un rischio di overflow se il numeratore della base dei tempi è abbastanza grande. Se qualcuno è curioso, c'è un link con molte più informazioni qui: https://stackoverflow.com/a/23378064/588476

Alla luce di tali informazioni, forse è più sicuro utilizzare la funzione di Apple CACurrentMediaTime!

Ho anche confrontato la mach_timebase_infochiamata con un benchmark e ci vogliono circa 19ns sul mio iPhone 6, quindi ho rimosso il codice (non thread-safe) che stava memorizzando nella cache l'output di quella chiamata.

#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;
}

Prestare attenzione al potenziale rischio di overflow a seconda dell'output della chiamata alla base dei tempi. Sospetto (ma non lo so) che potrebbe essere una costante per ogni modello di iPhone. sul mio iPhone 6 lo era 125/3.

La soluzione che utilizza CACurrentMediaTime()è abbastanza banale:

uint64_t getTickCount(void)
{
    double ret = CACurrentMediaTime();
    return ret * 1000;
}

Qualcuno sa perché c'è un cast (vuoto) ridondante nella chiamata mach_timebase_info?
Simon Tillson,

@SimonTillson questa è una buona domanda: o era necessario per mettere a tacere un avvertimento o l'ho copiato da qualche altra parte e non mi ero accorto di cancellarlo. Non ricordo.
Wayne Uroda,

2
Questo non è thread-safe. mach_timebase_info()azzererà passata-in mach_timebase_info_data_tper {0,0}prima di impostarlo a valori reali, che possono causare la divisione per zero se il codice viene chiamato da più thread. Usa dispatch_once()invece (o CACurrentMediaTimeche è solo il tempo mach convertito in secondi).
wonder.mice,

Grazie @ wonder.mice, ho aggiornato la mia risposta (ho riscontrato anche un problema relativo alla quantizzazione, incolpo me di 6 anni più giovane di me ...)
Wayne Uroda,


4

utilizzare la funzione timeIntervalSince1970 della classe NSDate come di seguito:

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

fondamentalmente, questo è quello che uso per confrontare la differenza in secondi tra 2 date diverse. controlla anche questo link qui


0

Le altre risposte sono corrette (con un avvertimento *). Aggiungo questa risposta semplicemente per mostrare un esempio di utilizzo:

- (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.
}

Sulla console di debugger, vedi qualcosa del genere:

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

* Avvertenza: come altri hanno già detto, utilizzare NSDate per calcolare il tempo trascorso solo per scopi casuali. Uno di questi scopi potrebbe essere il test comune, la profilazione grezza, in cui si desidera solo un'idea approssimativa di quanto tempo impiega un metodo.

Il rischio è che l'impostazione dell'ora corrente dell'orologio del dispositivo possa cambiare in qualsiasi momento a causa della sincronizzazione dell'orologio di rete. Quindi il NSDatetempo potrebbe saltare avanti o indietro in qualsiasi momento.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.