Potrzebuję czasu, jaki upłynął między dwoma zdarzeniami, na przykład pojawieniem się UIView i pierwszą reakcją użytkownika.
Jak mogę to osiągnąć w Objective-C?
Potrzebuję czasu, jaki upłynął między dwoma zdarzeniami, na przykład pojawieniem się UIView i pierwszą reakcją użytkownika.
Jak mogę to osiągnąć w Objective-C?
Odpowiedzi:
NSDate *start = [NSDate date];
// do stuff...
NSTimeInterval timeInterval = [start timeIntervalSinceNow];
timeInterval
to różnica między startem a teraz, w sekundach, z dokładnością poniżej milisekundy.
[NSDate date]
może prowadzić do trudnych do śledzenia błędów, zobacz tę odpowiedź, aby uzyskać więcej informacji.
Nie należy polegać na [NSDate date]
czasie, ponieważ może on zawyżać lub zaniżać czas, który upłynął. Istnieją nawet przypadki, w których twój komputer pozornie podróżuje w czasie, ponieważ upływający czas będzie ujemny! (Np. Jeśli zegar przesunął się do tyłu podczas pomiaru czasu).
Według Arii Haghighi w wykładzie „Advanced iOS Gesture Recognition” na kursie Winter 2013 Stanford iOS (34:00), CACurrentMediaTime()
jeśli potrzebujesz dokładnego interwału czasowego , powinieneś używać .
Cel C:
#import <QuartzCore/QuartzCore.h>
CFTimeInterval startTime = CACurrentMediaTime();
// perform some action
CFTimeInterval elapsedTime = CACurrentMediaTime() - startTime;
Szybki:
let startTime = CACurrentMediaTime()
// perform some action
let elapsedTime = CACurrentMediaTime() - startTime
Powodem jest to, że [NSDate date]
synchronizuje się na serwerze, więc może to prowadzić do „problemów z synchronizacją czasu”, co może prowadzić do bardzo trudnych do śledzenia błędów. CACurrentMediaTime()
z drugiej strony jest to czas urządzenia, który nie zmienia się podczas tych synchronizacji sieciowych.
Będziesz musiał dodać strukturę QuartzCore do ustawień celu.
[NSDate date]
w bloku uzupełniania w bloku wysyłania otrzymywałem ten sam „bieżący” czas, który był zgłaszany [NSDate date]
bezpośrednio przed wykonaniem punktu, w którym zostały utworzone moje bloki. CACurrentMediaTime()
rozwiązał ten problem.
CACurrentMediaTime()
przestaje tykać, gdy urządzenie przechodzi w stan uśpienia. Jeśli testujesz urządzenie odłączone od komputera, zablokuj je, a następnie odczekaj ~ 10 minut, a okaże się, że CACurrentMediaTime()
nie nadąża za zegarem ściennym.
Użyj timeIntervalSinceDate
metody
NSTimeInterval secondsElapsed = [secondDate timeIntervalSinceDate:firstDate];
NSTimeInterval
to tylko a double
, zdefiniuj w NSDate
ten sposób:
typedef double NSTimeInterval;
Dla każdego, kto szuka implementacji getTickCount () dla iOS, oto moje po połączeniu różnych źródeł.
Wcześniej miałem błąd w tym kodzie (najpierw podzieliłem przez 1000000), który powodował kwantyzację danych wyjściowych na moim iPhonie 6 (być może nie był to problem na iPhonie 4 / itp. Lub po prostu nigdy tego nie zauważyłem). Zwróć uwagę, że nie wykonując najpierw tego podziału, istnieje pewne ryzyko przepełnienia, jeśli licznik podstawy czasu jest dość duży. Jeśli ktoś jest ciekawy, tutaj znajduje się link z wieloma dodatkowymi informacjami: https://stackoverflow.com/a/23378064/588476
W świetle tych informacji może bezpieczniej jest korzystać z funkcji Apple CACurrentMediaTime
!
mach_timebase_info
Przeprowadziłem również test porównawczy połączenia i na moim iPhonie 6 zajmuje to około 19ns, więc usunąłem (nie zabezpieczony wątkami) kod, który buforował dane wyjściowe tego połączenia.
#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;
}
Należy być świadomym potencjalnego ryzyka przepełnienia w zależności od wyniku wywołania podstawy czasu. Podejrzewam (ale nie wiem), że może to być stała dla każdego modelu iPhone'a. na moim iPhonie 6 to było 125/3
.
Zastosowanie rozwiązania CACurrentMediaTime()
jest dość trywialne:
uint64_t getTickCount(void)
{
double ret = CACurrentMediaTime();
return ret * 1000;
}
mach_timebase_info()
zresetuje przekazywane w mach_timebase_info_data_t
celu {0,0}
przed ustawieniem go do rzeczywistych wartości, które mogą spowodować dzielenie przez zero, jeśli kod jest wywoływana z wielu wątków. Użyj dispatch_once()
zamiast tego (lub CACurrentMediaTime
który jest po prostu czasem mach przekonwertowanym na sekundy).
Aby uzyskać dokładne pomiary czasu (takie jak GetTickCount), zapoznaj się również z mach_absolute_time oraz z pytaniami i odpowiedziami firmy Apple: http://developer.apple.com/qa/qa2004/qa1398.html .
użyj funkcji timeIntervalSince1970 klasy NSDate jak poniżej:
double start = [startDate timeIntervalSince1970];
double end = [endDate timeIntervalSince1970];
double difference = end - start;
w zasadzie to właśnie używam do porównania różnicy w sekundach między 2 różnymi datami. sprawdź również ten link tutaj
Pozostałe odpowiedzi są poprawne (z zastrzeżeniem *). Dodaję tę odpowiedź, aby pokazać przykładowe użycie:
- (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.
}
W konsoli debugera zobaczysz coś takiego:
DEBUG Method '-[XMAppDelegate getYourAffairsInOrder]' ran. Elapsed: 0.033827 seconds.
* Uwaga: jak wspominali inni, używaj NSDate
do obliczania czasu, który upłynął, tylko do celów dorywczych. Jednym z takich celów może być powszechne testowanie, prymitywne profilowanie, w którym chcesz po prostu uzyskać ogólne pojęcie o tym, jak długo trwa metoda.
Istnieje ryzyko, że bieżące ustawienie zegara urządzenia może się zmienić w dowolnym momencie z powodu synchronizacji zegara sieciowego. NSDate
Czas mógł więc w każdej chwili przeskoczyć do przodu lub do tyłu.
fabs(...)
aby uzyskać wartość bezwzględną pływaka. Na przykład.NSTimeInterval timeInterval = fabs([start timeIntervalSinceNow]);