Obtenir le temps écoulé dans Objective-C

153

J'ai besoin d'obtenir le temps écoulé entre deux événements, par exemple, l'apparition d'un UIView et la première réaction de l'utilisateur.

Comment puis-je y parvenir en Objective-C?

Ilya Suzdalnitski
la source

Réponses:

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

timeInterval est la différence entre début et maintenant, en secondes, avec une précision inférieure à la milliseconde.

Can Berk Güder
la source
18
@NicolasZozol vous pouvez utiliser fabs(...)pour obtenir la valeur absolue d'un flottant. Par exemple. NSTimeInterval timeInterval = fabs([start timeIntervalSinceNow]);
So Over It
1
semble que la valeur de timeInterval soit négative.
Jacky
si vous obtenez une valeur négative - multipliez par -1 et vous êtes bon
PinkFloydRocks
10
S'appuyer sur [NSDate date]peut conduire à des bogues difficiles à suivre, voir cette réponse pour plus d'informations.
Senseful
@PinkFloydRocks - Quoi? Comment une valeur négative se produirait-elle légitimement?
Todd Lehman
228

Vous ne devez pas vous en remettre à [NSDate date]des fins de chronométrage car il peut sur-ou sous-déclarer le temps écoulé. Il y a même des cas où votre ordinateur va apparemment voyager dans le temps car le temps écoulé sera négatif! (Par exemple, si l'horloge a reculé pendant le chronométrage.)

Selon Aria Haghighi dans la conférence "Advanced iOS Gesture Recognition" du cours Winter 2013 Stanford iOS (34:00), vous devriez utiliser CACurrentMediaTime()si vous avez besoin d'un intervalle de temps précis.

Objectif c:

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

Rapide:

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

La raison en est que se [NSDate date]synchronise sur le serveur, cela peut donc conduire à des «hoquets de synchronisation temporelle» qui peuvent conduire à des bogues très difficiles à suivre. CACurrentMediaTime(), d'autre part, est une heure de l'appareil qui ne change pas avec ces synchronisations réseau.

Vous devrez ajouter le framework QuartzCore aux paramètres de votre cible.

Sensé
la source
17
Veuillez voter pour cela. Bien que je pense que tout le monde peut convenir que NSDate devrait être votre première option, cette suggestion a résolu un de mes problèmes. Lors de l'appel à l' [NSDate date]intérieur d'un bloc d'achèvement dans un bloc de répartition, j'obtenais la même heure "actuelle" que celle signalée [NSDate date]juste avant que l'exécution n'atteigne le point auquel mes blocs ont été créés. CACurrentMediaTime()résolu ce problème.
n00neimp0rtant
Comment utiliserions-nous elapsedTime pour un affichage comme mm: ss?
CyberMew
@CyberMew: C'est un problème distinct. Voir Comment décomposer un NSTimeInterval en année, mois, jours, heures, minutes et secondes sur iPhone? pour certaines solutions.
Senseful
12
Sachez que CACurrentMediaTime()cesse de clignoter lorsque l'appareil entre en veille. Si vous testez avec l'appareil déconnecté d'un ordinateur, verrouillez-le, puis attendez ~ 10 minutes, vous constaterez que CACurrentMediaTime()cela ne correspond pas à l'heure de l'horloge murale.
russbishop
ajouter et importer #import <QuartzCore / QuartzCore.h>
steveen zoleko
23

Utilisez la timeIntervalSinceDateméthode

NSTimeInterval secondsElapsed = [secondDate timeIntervalSinceDate:firstDate];

NSTimeIntervalest juste un double, définissez NSDatecomme ceci:

typedef double NSTimeInterval;
Marco Lazzeri
la source
15

Pour quiconque vient ici à la recherche d'une implémentation getTickCount () pour iOS, voici la mienne après avoir rassemblé diverses sources.

Auparavant, j'avais un bug dans ce code (je divisais d'abord par 1000000) qui provoquait une quantification de la sortie sur mon iPhone 6 (peut-être que ce n'était pas un problème sur iPhone 4 / etc ou je ne l'ai tout simplement jamais remarqué). Notez qu'en n'effectuant pas cette division en premier, il y a un risque de débordement si le numérateur de la base de temps est assez grand. Si quelqu'un est curieux, il y a un lien avec beaucoup plus d'informations ici: https://stackoverflow.com/a/23378064/588476

À la lumière de ces informations, il est peut-être plus sûr d'utiliser la fonction Apple CACurrentMediaTime!

J'ai également évalué l' mach_timebase_infoappel et cela prend environ 19ns sur mon iPhone 6, j'ai donc supprimé le code (non threadsafe) qui mettait en cache la sortie de cet appel.

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

Soyez conscient du risque potentiel de dépassement de capacité en fonction de la sortie de l'appel de base de temps. Je soupçonne (mais je ne sais pas) que cela pourrait être une constante pour chaque modèle d'iPhone. sur mon iPhone 6 c'était 125/3.

La solution à l'aide CACurrentMediaTime()est assez triviale:

uint64_t getTickCount(void)
{
    double ret = CACurrentMediaTime();
    return ret * 1000;
}
Wayne Uroda
la source
Quelqu'un sait pourquoi il y a un cast redondant (vide) sur l'appel mach_timebase_info?
Simon Tillson
@SimonTillson c'est une bonne question - soit il était nécessaire de faire taire un avertissement, soit je l'ai copié ailleurs et je n'ai tout simplement pas remarqué de le supprimer. Je ne m'en souviens plus.
Wayne Uroda
2
Ce n'est pas thread-safe. mach_timebase_info()réinitialisera passé mach_timebase_info_data_tà {0,0}avant de le définir sur les valeurs réelles, ce qui peut entraîner une division par zéro si le code est appelé à partir de plusieurs threads. Utilisez à la dispatch_once()place (ou CACurrentMediaTimequi est juste le temps mach converti en secondes).
wonder.mice
Merci @ wonder.mice, j'ai mis à jour ma réponse (j'ai également trouvé un problème concernant la quantification, je blâme moi-même 6 ans plus jeune ...)
Wayne Uroda
4

utilisez la fonction timeIntervalSince1970 de la classe NSDate comme ci-dessous:

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

en gros, c'est ce que j'utilise pour comparer la différence en secondes entre 2 dates différentes. vérifiez également ce lien ici

Raj
la source
0

Les autres réponses sont correctes (avec une mise en garde *). J'ajoute cette réponse simplement pour montrer un exemple d'utilisation:

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

Sur la console du débogueur, vous voyez quelque chose comme ceci:

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

* Avertissement: comme d'autres l'ont mentionné, utilisez NSDatepour calculer le temps écoulé uniquement à des fins occasionnelles. L'un de ces objectifs pourrait être des tests courants, un profilage brut, où vous voulez juste avoir une idée approximative de la durée d'une méthode.

Le risque est que le réglage de l'heure actuelle de l'horloge de l'appareil puisse changer à tout moment en raison de la synchronisation de l'horloge du réseau. Le NSDatetemps pouvait donc avancer ou reculer à tout moment.

Basil Bourque
la source