Existe-t-il un moyen facile d'obtenir une heure très précisément?
J'ai besoin de calculer certains délais entre les appels de méthode. Plus précisément, je souhaite calculer la vitesse de défilement dans un UIScrollView.
Existe-t-il un moyen facile d'obtenir une heure très précisément?
J'ai besoin de calculer certains délais entre les appels de méthode. Plus précisément, je souhaite calculer la vitesse de défilement dans un UIScrollView.
Réponses:
NSDate
et les timeIntervalSince*
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 du temps en millisecondes, vous pouvez faire:
// 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 existe de nombreuses autres façons de calculer cet intervalle en utilisant NSDate
, et je recommanderais NSDate
de consulter la documentation de classe pour laquelle se trouve dans NSDate Class Reference .
NSDate
et mach_absolute_time()
au niveau d'environ 30ms. 27 vs 29, 36 vs 39, 43 vs 45. NSDate
était plus facile à utiliser pour moi et les résultats étaient assez similaires pour ne pas s'en soucier.
mach_absolute_time()
peut être utilisé pour obtenir des mesures précises.
Voir http://developer.apple.com/qa/qa2004/qa1398.html
Est également disponible CACurrentMediaTime()
, qui est essentiellement la même chose mais avec une interface plus facile à utiliser.
(Remarque: cette réponse a été rédigée en 2009. Voir la réponse de Pavel Alexeev pour les clock_gettime()
interfaces POSIX plus simples disponibles dans les nouvelles versions de macOS et iOS.)
CACurrentMediaTime()
, qui se convertit mach_absolute_time()
directement en fichier double
.
elapsedNano
est de type Nanoseconds
, qui n'est pas un type entier. C'est un alias de UnsignedWide
, qui est une structure avec deux champs entiers 32 bits. Vous pouvez utiliser à la UnsignedWideToUInt64()
place du casting si vous préférez.
Veuillez ne pas utiliser NSDate
, CFAbsoluteTimeGetCurrent
ou gettimeofday
pour mesurer le temps écoulé. Tout cela dépend de l'horloge système, qui peut changer à tout moment pour de nombreuses raisons différentes, telles que la synchronisation de l'heure du réseau (NTP), la mise à jour de l'horloge (il arrive souvent à s'ajuster à la dérive), les ajustements d'heure d'été, les secondes intercalaires, etc.
Cela signifie que si vous mesurez votre vitesse de téléchargement ou de téléchargement, vous obtiendrez des pics ou des baisses soudaines de vos chiffres qui ne correspondent pas à ce qui s'est réellement passé; vos tests de performance auront des valeurs aberrantes incorrectes; et vos minuteries manuelles se déclencheront après des durées incorrectes. Le temps peut même reculer, 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).
Utilisez mach_absolute_time
. Il mesure de vraies secondes depuis le démarrage du noyau. Il augmente de manière monotone (ne recule jamais) et n'est pas affecté par les paramètres de date et d'heure. Comme il est difficile de travailler avec, voici un simple wrapper qui vous donne NSTimeInterval
:
// 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
CACurrentMediaTime()
de QuartzCore?
@import Darwin;
place de#include <mach/mach_time.h>
CFAbsoluteTimeGetCurrent()
renvoie le temps absolu sous forme de double
valeur, mais je ne sais pas quelle est sa précision - il peut ne se mettre à jour que toutes les douzaines de millisecondes, ou il peut se mettre à jour toutes les microsecondes, je ne sais pas.
72.89674947369
secondes serait assez rare, compte tenu de toutes les autres valeurs qu'elle pourrait être. ;)
Je n'utiliserais PAS mach_absolute_time()
car il interroge une combinaison du noyau et du processeur pendant un temps absolu en utilisant des ticks (probablement un temps de disponibilité).
Ce que j'utiliserais:
CFAbsoluteTimeGetCurrent();
Cette fonction est optimisée pour corriger la différence entre le logiciel et le matériel iOS et OSX.
Quelque chose de geekier
Le quotient d'une différence entre mach_absolute_time()
et AFAbsoluteTimeGetCurrent()
est toujours autour de 24000011.154871
Voici un journal de mon application:
S'il vous plaît noter que le temps de résultat final est une différence dans CFAbsoluteTimeGetCurrent()
« s
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] -----------------------------------------------------
mach_absolute_time()
avec un mach_timebase_info_data
et puis je l'ai fait (long double)((mach_absolute_time()*time_base.numer)/((1000*1000)*time_base.denom));
. Pour obtenir la base de temps mach, vous pouvez faire(void)mach_timebase_info(&your_timebase);
#define CTTimeStart() NSDate * __date = [NSDate date]
#define CTTimeEnd(MSG) NSLog(MSG " %g",[__date timeIntervalSinceNow]*-1)
Usage:
CTTimeStart();
...
CTTimeEnd(@"that was a long time:");
Production:
2013-08-23 15:34:39.558 App-Dev[21229:907] that was a long time: .0023
NSDate
dépend de l'horloge du système qui peut être modifiée à tout moment, ce qui peut entraîner des intervalles de temps erronés ou même négatifs. Utilisez à la mach_absolute_time
place pour obtenir le temps écoulé correct.
mach_absolute_time
peut être affecté par les redémarrages de l'appareil. Utilisez plutôt l'heure du serveur.
Aussi, voici comment calculer un 64 bits NSNumber
initialisé avec l'époque Unix en millisecondes, au cas où vous voudriez le stocker dans CoreData. J'en avais besoin 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
sont bonnes pour les mesures courtes.
Mais pour les longues mesures, une mise en garde importante est qu'ils cessent de tourner pendant que l'appareil est en veille.
Il existe une fonction pour obtenir le temps depuis le démarrage. Cela ne s'arrête pas pendant le sommeil. De plus, ce gettimeofday
n'est pas monotone, mais dans mes expériences, j'ai toujours vu que l'heure de démarrage change lorsque l'heure du système change, donc je pense 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()
- pas monotone, peut revenir en arrièreCACurrentMediaTime()
- arrête de tic-tac lorsque l'appareil est en veilletimeSinceBoot()
- ne dort pas, mais n'est peut-être pas monotoneCLOCK_MONOTONIC
- ne dort pas, monotone, pris en charge depuis iOS 10Je sais que c'est un ancien, mais même moi, je me suis retrouvé à errer à nouveau, alors j'ai pensé que je soumettrais ma propre option ici.
Le mieux est de consulter mon article de blog à ce sujet: Chronométrage en Objective-C: Un chronomètre
Fondamentalement, j'ai écrit une classe qui arrête de regarder de manière très basique mais qui est encapsulée afin que vous n'ayez qu'à 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 article pour un peu plus ou téléchargez-le ici: MMStopwatch.zip
NSDate
dépend de l'horloge du système qui peut être modifiée à tout moment, ce qui peut entraîner des intervalles de temps erronés ou même négatifs. Utilisez à la mach_absolute_time
place pour obtenir le temps écoulé correct.
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;
}
Pour ceux-ci, nous avons besoin de la version Swift de la réponse de @Jeff Thompson:
// Get a current time for where you want to start measuring from
var date = NSDate()
// do work...
// Find elapsed time and convert to milliseconds
// Use (-) modifier to conversion since receiver is earlier than now
var timePassed_ms: Double = date.timeIntervalSinceNow * -1000.0
J'espère que cela vous aidera.
NSDate
dépend de l'horloge du système qui peut être modifiée à tout moment, ce qui peut entraîner des intervalles de temps erronés ou même négatifs. Utilisez à la mach_absolute_time
place pour obtenir le temps écoulé correct.