Как я могу получить точное время, например, в миллисекундах в Objective-C?
Есть ли простой способ получить точное время?
Мне нужно рассчитать некоторые задержки между вызовами метода. В частности, я хочу рассчитать скорость прокрутки в UIScrollView.
- objective C: Как вы можете повернуть текст для UIButton и UILabel?
- Неверное завершение кода Xcode 6 не работает
- Перетаскивание UIView внутри UIScrollView
- Как распределить приложение ios без проводов без управления UDID и перекомпиляции
- UIImage imageNamed требует pathForResource?
- Как использовать первый символ в качестве имени раздела
- Очереди отправки: как узнать, запущены ли они и как их остановить
- Странная проблема сравнения поплавков в объективе-C
- iOS 5: Как преобразовать Emoji в символ Юникода?
- Как масштабировать UIImageView пропорционально?
- Как я могу «разрезать» прозрачное отверстие в UIImage?
- iPhone: обнаружение бездействия пользователя / время простоя с момента последнего касания экрана
- Синглтон в iOS 5?
NSDate
и методы timeIntervalSince*
возвращают NSTimeInterval
который является двойным с точностью до миллисекунды. NSTimeInterval
находится в секундах, но он использует двойной, чтобы дать вам большую точность.
Чтобы вычислить миллисекундную точность времени, вы можете:
// 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;
Документация по времениIntervalSinceNow .
Существует много других способов расчета этого интервала с использованием NSDate
, и я бы рекомендовал посмотреть документацию classа для NSDate
которая содержится в NSDate Class Reference .
Для получения точных измерений можно использовать mach_absolute_time()
.
См. http://developer.apple.com/qa/qa2004/qa1398.html
Также доступен CACurrentMediaTime()
, что по сути то же самое, но с более простым в использовании интерфейсом.
Пожалуйста, не используйте NSDate
, CFAbsoluteTimeGetCurrent
или gettimeofday
для измерения прошедшего времени. Все это зависит от системных часов, которые могут меняться в любое время по многим причинам, например, синхронизация времени в сети (NTP), обновляющая часы (часто приходится настраивать для дрейфа), регулировки DST, секунды прыжка и т. Д.
Это означает, что если вы измеряете скорость загрузки или выгрузки, вы получите внезапные всплески или падения в ваших номерах, которые не коррелируют с тем, что на самом деле произошло; ваши тесты производительности будут иметь странные неправильные выбросы; и ваши ручные таймеры будут срабатывать после неправильной продолжительности. Время может даже вернуться назад, и вы окажетесь в отрицательных дельтах, и вы можете обрести бесконечную рекурсию или мертвый код (да, я сделал это оба).
Используйте mach_absolute_time
. Он измеряет реальные секунды с момента загрузки ядра. Он monoтонно увеличивается (никогда не будет возвращаться назад) и не зависит от настроек даты и времени. Поскольку это боль для работы, вот простая shell, которая дает вам 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 #include @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()
возвращает абсолютное время как double
значение, но я не знаю, какова его точность: он может обновляться только каждые дюжины миллисекунд или может обновлять каждую микросекунду, я не знаю.
Я бы не использовал mach_absolute_time()
потому что он запрашивает комбинацию ядра и процессора в течение абсолютного времени с использованием тиков (возможно, время безотказной работы).
Что я буду использовать:
CFAbsoluteTimeGetCurrent();
Эта функция оптимизирована для исправления различий в программном и аппаратном обеспечении iOS и OSX.
Что-то Geekier
Фактор разницы в mach_absolute_time()
и AFAbsoluteTimeGetCurrent()
всегда равен 24000011.154871
Вот журнал моего приложения:
Обратите внимание, что конечное время результата – это разница в 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)
Применение:
CTTimeStart(); ... CTTimeEnd(@"that was a long time:");
Вывод:
2013-08-23 15:34:39.558 App-Dev[21229:907] that was a long time: .0023
Кроме того, вот как вычислить 64-разрядный NSNumber
инициализированный с эпохой Unix в миллисекундах, в случае, если вы хотите сохранить его в CoreData. Мне это нужно для моего приложения, которое взаимодействует с системой, которая хранит даты таким образом.
+ (NSNumber*) longUnixEpoch { return [NSNumber numberWithLongLong:[[NSDate date] timeIntervalSince1970] * 1000]; }
Я знаю, что это старый, но даже я обнаружил, что снова блуждаю по нему, поэтому я подумал, что я предлагаю свой вариант здесь.
Лучше всего проверить мой пост в блоге по этому поводу : Сроки в Objective-C: секундомер
В принципе, я написал class, который перестает смотреть очень простым способом, но инкапсулирован так, что вам нужно только сделать следующее:
[MMStopwatchARC start:@"My Timer"]; // your work here ... [MMStopwatchARC stop:@"My Timer"];
И вы получите:
MyApp[4090:15203] -> Stopwatch: [My Timer] runtime: [0.029]
в журнале …
Опять же, просмотрите мой пост еще немного или загрузите его здесь: MMStopwatch.zip
Функции, основанные на mach_absolute_time
, хороши для коротких измерений.
Но для длительных измерений важно остерегаться того, что они перестают тикать, пока устройство спит.
Существует функция получения времени с момента загрузки. Он не останавливается во время сна. Кроме того, gettimeofday
не является monoтонным, но в моих экспериментах я всегда вижу, что время загрузки изменяется при изменении системного времени, поэтому я думаю, что он должен работать нормально.
func timeSinceBoot() -> TimeInterval { var bootTime = timeval() var currentTime = timeval() var timeZone = timezone() let mib = UnsafeMutablePointer.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 }
А поскольку iOS 10 и macOS 10.12 можно использовать 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 } }
Подвести итог:
-
Date.timeIntervalSinceReferenceDate
– изменяется при изменении системного времени, а не monoтонном -
CFAbsoluteTimeGetCurrent()
– не monoтонно, может идти назад -
CACurrentMediaTime()
– останавливает тикинг, когда устройство спит -
timeSinceBoot()
– не спит, но может быть не monoтонным -
CLOCK_MONOTONIC
– не спит, monoтонно поддерживается с iOS 10
Для тех, кому нужна быстрая версия ответа @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
Надеюсь, это поможет вам.
Вы можете получить текущее время в миллисекундах с 1 января 1970 года, используя NSDate:
- (double)currentTimeInMilliseconds { NSDate *date = [NSDate date]; return [date timeIntervalSince1970]*1000; }