web-dev-qa-db-fra.com

Comment puis-je obtenir une heure précise, par exemple en millisecondes dans Objective-C?

Existe-t-il un moyen facile d'obtenir un temps très précis?

Je dois calculer des délais entre les appels de méthode. Plus précisément, je souhaite calculer la vitesse de défilement dans un UIScrollView.

97
Thanks

NSDate et le timeIntervalSince* Les méthodes renverront un NSTimeInterval qui est un double avec une précision inférieure à la milliseconde. NSTimeInterval est en secondes, mais il utilise le double pour vous donner une plus grande précision.

Pour calculer la précision temporelle en millisecondes, vous pouvez effectuer les opérations suivantes:

// 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;

Documentation sur timeIntervalSinceNow .

Il y a beaucoup d'autres façons de calculer cet intervalle en utilisant NSDate, et je vous conseillerais de consulter la documentation de classe pour NSDate qui se trouve dans Référence de classe NSDate.

126
Jeff Thompson

mach_absolute_time() peut être utilisé pour obtenir des mesures précises.

Voir http://developer.Apple.com/qa/qa2004/qa1398.html

La fonction CACurrentMediaTime() est également disponible, ce qui revient essentiellement au même, mais avec une interface plus facile à utiliser.

40

Veuillez ne pas utiliser NSDate, CFAbsoluteTimeGetCurrent ou gettimeofday pour mesurer le temps écoulé. Celles-ci dépendent toutes de l'horloge système, qui peut changer à à tout moment pour diverses raisons, telles que la synchronisation de l'heure du réseau (NTP) mettant à jour l'horloge ( se produit souvent pour tenir compte de la dérive), des ajustements de l'heure d'été, des secondes intercalaires, etc.

Cela signifie que si vous mesurez votre vitesse de téléchargement ou de téléchargement, vous obtiendrez des pointes ou des baisses soudaines dans vos chiffres qui ne correspondraient pas à ce qui s'est réellement passé. vos tests de performance auront d'étranges valeurs aberrantes incorrectes; et vos temporisateurs manuels se déclencheront après des durées incorrectes. Le temps peut même aller en arrière et vous vous retrouvez avec des deltas négatifs, et vous pouvez vous retrouver avec une récursion infinie ou un code mort (oui, j'ai fait les deux).

Utilisation mach_absolute_time. Il mesure les secondes réelles depuis le démarrage du noyau. Il augmente de façon monotone (ne reviendra jamais en arrière) et n'est pas affecté par les paramètres de date et d'heure. Comme il est difficile de travailler avec, voici un wrapper simple qui vous donne NSTimeIntervals:

// 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
26
nevyn

CFAbsoluteTimeGetCurrent() renvoie le temps absolu sous forme de valeur double, mais je ne sais pas quelle est sa précision - il pourrait ne mettre à jour que toutes les dizaines de millisecondes, ou peut-être mettre à jour toutes les microsecondes, je ne sais pas.

14
Adam Rosenfield

Je n'utiliserais PAS mach_absolute_time() parce qu'il interroge une combinaison du noyau et du processeur pendant un temps absolu à l'aide de ticks (probablement un temps de disponibilité).

Ce que j'utiliserais:

CFAbsoluteTimeGetCurrent();

Cette fonction est optimisée pour corriger la différence entre les logiciels et le matériel iOS et OSX.

Quelque chose de geekier

Le quotient d'une différence dans mach_absolute_time() et AFAbsoluteTimeGetCurrent() est toujours autour de 24000011.154871

Voici un journal de mon application:

Veuillez noter que le temps de résultat final est une différence entre 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] -----------------------------------------------------
10
Nate Symer
#define CTTimeStart() NSDate * __date = [NSDate date]
#define CTTimeEnd(MSG) NSLog(MSG " %g",[__date timeIntervalSinceNow]*-1)

Usage:

CTTimeStart();
...
CTTimeEnd(@"that was a long time:");

Sortie:

2013-08-23 15:34:39.558 App-Dev[21229:907] that was a long time: .0023
6
gngrwzrd

En outre, voici comment calculer un NSNumber 64 bits initialisé avec l’époque Unix en millisecondes, au cas où vous souhaiteriez le stocker dans CoreData. J'avais besoin de cela pour mon application qui interagit avec un système qui stocke les dates de cette façon.

  + (NSNumber*) longUnixEpoch {
      return [NSNumber numberWithLongLong:[[NSDate date] timeIntervalSince1970] * 1000];
  }
4
John Wright

Les fonctions basées sur mach_absolute_time Conviennent aux mesures courtes.
Mais pour les longues mesures, il est important de noter qu’ils cessent de fonctionner lorsque l’appareil est en veille.

Il y a une fonction pour obtenir du temps depuis le démarrage. Ça ne s'arrête pas en dormant. De plus, gettimeofday n’est pas monotone, mais dans mes expériences, j’ai toujours constaté que l’heure de démarrage change en même temps que l’heure du système. Je pense donc que cela devrait fonctionner correctement.

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(&currentTime, &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
}

Et depuis iOS 10 et macOS 10.12, nous pouvons utiliser 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
    }
}

Résumer:

  • Date.timeIntervalSinceReferenceDate - change lorsque l'heure du système change, pas monotone
  • CFAbsoluteTimeGetCurrent() - non monotone, peut revenir en arrière
  • CACurrentMediaTime() - arrête la lecture lorsque l'appareil est en veille
  • timeSinceBoot() - ne dort pas, mais n'est peut-être pas monotone
  • CLOCK_MONOTONIC - ne dort pas, monotone, supporté depuis iOS 10
2
Pavel Alexeev

Vous pouvez obtenir l'heure actuelle en millisecondes depuis le 1er janvier 1970 en utilisant un NSDate:

- (double)currentTimeInMilliseconds {
    NSDate *date = [NSDate date];
    return [date timeIntervalSince1970]*1000;
}
1
Camilo Ortegón

Je sais que c’est un vieux projet, mais même si je me suis retrouvé à errer à nouveau, j’ai donc pensé soumettre ma propre option ici.

Le mieux est de consulter mon article de blog à ce sujet: Chronométrer les choses dans Objective-C: un chronomètre

Fondamentalement, j’ai écrit un cours qui arrête de regarder de façon très basique mais qui est encapsulé pour que vous n’ayez besoin que de faire ce qui suit:

[MMStopwatchARC start:@"My Timer"];
// your work here ...
[MMStopwatchARC stop:@"My Timer"];

Et vous vous retrouvez avec:

MyApp[4090:15203]  -> Stopwatch: [My Timer] runtime: [0.029]

dans le journal ...

Encore une fois, consultez mon post pour un peu plus ou téléchargez-le ici: MMStopwatch.Zip

1
bladnman