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.
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.
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.
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 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
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.
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] -----------------------------------------------------
#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
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];
}
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(¤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
}
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 monotoneCFAbsoluteTimeGetCurrent()
- non monotone, peut revenir en arrièreCACurrentMediaTime()
- arrête la lecture lorsque l'appareil est en veilletimeSinceBoot()
- ne dort pas, mais n'est peut-être pas monotoneCLOCK_MONOTONIC
- ne dort pas, monotone, supporté depuis iOS 10Vous 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;
}
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