Ich muss die zwischen zwei Ereignissen verstrichene Zeit ermitteln, z. B. das Erscheinen eines UIView und die erste Reaktion des Benutzers.
Wie kann ich es in Objective-C erreichen?
Ich muss die zwischen zwei Ereignissen verstrichene Zeit ermitteln, z. B. das Erscheinen eines UIView und die erste Reaktion des Benutzers.
Wie kann ich es in Objective-C erreichen?
Antworten:
NSDate *start = [NSDate date];
// do stuff...
NSTimeInterval timeInterval = [start timeIntervalSinceNow];
timeInterval
ist der Unterschied zwischen Start und Jetzt in Sekunden mit einer Genauigkeit von weniger als einer Millisekunde.
[NSDate date]
kann dies zu schwer zu verfolgenden Fehlern führen. Weitere Informationen finden Sie in dieser Antwort .
Sie sollten sich nicht auf [NSDate date]
Timing-Zwecke verlassen, da dies die verstrichene Zeit über- oder unterschätzen kann. Es gibt sogar Fälle, in denen Ihr Computer scheinbar eine Zeitreise unternimmt, da die verstrichene Zeit negativ ist! (ZB wenn sich die Uhr während des Timings rückwärts bewegte.)
Laut Aria Haghighi in der Vorlesung "Advanced iOS Gesture Recognition" des Stanford iOS-Kurses Winter 2013 (34:00) sollten Sie diese verwenden, CACurrentMediaTime()
wenn Sie ein genaues Zeitintervall benötigen.
Ziel c:
#import <QuartzCore/QuartzCore.h>
CFTimeInterval startTime = CACurrentMediaTime();
// perform some action
CFTimeInterval elapsedTime = CACurrentMediaTime() - startTime;
Schnell:
let startTime = CACurrentMediaTime()
// perform some action
let elapsedTime = CACurrentMediaTime() - startTime
Der Grund dafür ist, dass die [NSDate date]
Synchronisierung auf dem Server zu "Zeitsynchronisierungsproblemen" führen kann, die zu sehr schwer zu verfolgenden Fehlern führen können. CACurrentMediaTime()
Andererseits ist dies eine Gerätezeit, die sich bei diesen Netzwerksynchronisierungen nicht ändert.
Sie müssen das QuartzCore-Framework zu den Einstellungen Ihres Ziels hinzufügen .
[NSDate date]
innerhalb eines Abschlussblocks innerhalb eines Versandblocks wurde dieselbe "aktuelle" Zeit angezeigt, die [NSDate date]
unmittelbar vor der Ausführung gemeldet wurde, als der Punkt erreicht wurde, an dem meine Blöcke erstellt wurden. CACurrentMediaTime()
Dieses Problem wurde behoben.
CACurrentMediaTime()
Ticken aufhört, wenn das Gerät in den Ruhezustand wechselt. Wenn Sie testen, während das Gerät von einem Computer getrennt ist, sperren Sie es und warten Sie ca. 10 Minuten. Sie werden feststellen, dass CACurrentMediaTime()
dies nicht mit der Wanduhrzeit Schritt hält.
Verwenden Sie die timeIntervalSinceDate
Methode
NSTimeInterval secondsElapsed = [secondDate timeIntervalSinceDate:firstDate];
NSTimeInterval
ist nur ein double
, definieren Sie in NSDate
wie folgt :
typedef double NSTimeInterval;
Für alle, die hierher kommen und nach einer getTickCount () - Implementierung für iOS suchen, ist hier meine, nachdem sie verschiedene Quellen zusammengestellt haben.
Zuvor hatte ich einen Fehler in diesem Code (ich habe zuerst durch 1000000 geteilt), der eine Quantisierung der Ausgabe auf meinem iPhone 6 verursachte (möglicherweise war dies kein Problem auf dem iPhone 4 / etc oder ich habe es einfach nie bemerkt). Beachten Sie, dass, wenn diese Division nicht zuerst ausgeführt wird, ein gewisses Überlaufrisiko besteht, wenn der Zähler der Zeitbasis ziemlich groß ist. Wenn jemand neugierig ist, gibt es hier einen Link mit viel mehr Informationen: https://stackoverflow.com/a/23378064/588476
Angesichts dieser Informationen ist es möglicherweise sicherer, die Apple-Funktion zu verwenden CACurrentMediaTime
!
Ich habe den mach_timebase_info
Anruf auch einem Benchmarking unterzogen und auf meinem iPhone 6 dauert es ungefähr 19 ns. Daher habe ich den (nicht threadsicheren) Code entfernt, der die Ausgabe dieses Anrufs zwischengespeichert hat.
#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;
}
Beachten Sie das potenzielle Risiko eines Überlaufs in Abhängigkeit von der Ausgabe des Zeitbasisaufrufs. Ich vermute (aber weiß nicht), dass es eine Konstante für jedes iPhone-Modell sein könnte. auf meinem iPhone 6 war es125/3
.
Die Lösung CACurrentMediaTime()
ist ziemlich trivial:
uint64_t getTickCount(void)
{
double ret = CACurrentMediaTime();
return ret * 1000;
}
mach_timebase_info()
setzt die Übergabe auf zurück mach_timebase_info_data_t
, {0,0}
bevor sie auf die tatsächlichen Werte gesetzt wird. Dies kann zu einer Division durch Null führen, wenn Code von mehreren Threads aufgerufen wird. Verwenden Sie dispatch_once()
stattdessen (oder CACurrentMediaTime
dies ist nur die in Sekunden umgerechnete Mach-Zeit).
Informationen zur genauen Zeitmessung (wie GetTickCount) finden Sie auch unter mach_absolute_time und in den Apple-Fragen und Antworten: http://developer.apple.com/qa/qa2004/qa1398.html .
Verwenden Sie die timeIntervalSince1970-Funktion der NSDate-Klasse wie folgt:
double start = [startDate timeIntervalSince1970];
double end = [endDate timeIntervalSince1970];
double difference = end - start;
Im Grunde ist es das, was ich benutze, um den Unterschied in Sekunden zwischen 2 verschiedenen Daten zu vergleichen. Überprüfen Sie auch diesen Link hier
Die anderen Antworten sind richtig (mit einer Einschränkung *). Ich füge diese Antwort einfach hinzu, um eine Beispielverwendung zu zeigen:
- (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.
}
Auf der Debugger-Konsole sehen Sie ungefähr Folgendes:
DEBUG Method '-[XMAppDelegate getYourAffairsInOrder]' ran. Elapsed: 0.033827 seconds.
* Vorsichtsmaßnahme: Verwenden Sie NSDate
, wie bereits erwähnt, die Berechnung der verstrichenen Zeit nur für gelegentliche Zwecke. Ein solcher Zweck könnte das allgemeine Testen sein, das grobe Profiling, bei dem Sie nur eine ungefähre Vorstellung davon haben möchten, wie lange eine Methode dauert.
Das Risiko besteht darin, dass sich die aktuelle Zeiteinstellung des Geräts aufgrund der Synchronisierung der Netzwerkuhr jederzeit ändern kann. Die NSDate
Zeit könnte also jederzeit vorwärts oder rückwärts springen.
fabs(...)
, um den absoluten Wert eines Floats zu erhalten. Z.B.NSTimeInterval timeInterval = fabs([start timeIntervalSinceNow]);