Как я могу получить точное время, например, в миллисекундах в Objective-C? - PullRequest
96 голосов
/ 20 мая 2009

Есть ли простой способ очень точно определить время?

Мне нужно рассчитать некоторые задержки между вызовами методов. Более конкретно, я хочу вычислить скорость прокрутки в UIScrollView.

Ответы [ 11 ]

126 голосов
/ 20 мая 2009

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, и я бы рекомендовал взглянуть на документацию класса для NSDate, которая находится в Ссылка на класс NSDate .

40 голосов
/ 20 мая 2009

mach_absolute_time() может использоваться для получения точных измерений.

См. http://developer.apple.com/qa/qa2004/qa1398.html

Также доступно CACurrentMediaTime(), что по сути то же самое, но с более простым в использовании интерфейсом.

26 голосов
/ 21 мая 2015

Пожалуйста, не используйте NSDate, CFAbsoluteTimeGetCurrent или gettimeofday для измерения прошедшего времени. Все они зависят от системных часов, которые могут меняться в любое время по многим различным причинам, таким как синхронизация сетевого времени (NTP), обновление часов (часто происходит с поправкой на смещение), настройки DST, скачок секунд и т. д.

Это означает, что если вы измеряете скорость загрузки или выгрузки, вы получите внезапные всплески или падения чисел, которые не коррелируют с тем, что произошло на самом деле; ваши тесты производительности будут иметь странные неправильные выбросы; и ваши ручные таймеры сработают после неправильной продолжительности. Время может даже пойти вспять, и вы получите отрицательные дельты, и вы можете получить бесконечную рекурсию или мертвый код (да, я сделал и то, и другое).

Используйте mach_absolute_time. Он измеряет реальные секунды с момента загрузки ядра. Он монотонно увеличивается (никогда не пойдет назад) и не зависит от настроек даты и времени. Так как работать с этим больно, вот простая оболочка, которая дает вам 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
14 голосов
/ 20 мая 2009

CFAbsoluteTimeGetCurrent() возвращает абсолютное время как значение double, но я не знаю, какова его точность - оно может обновлять только каждые дюжины миллисекунд или он может обновляться каждую микросекунду, я не знаю.

10 голосов
/ 20 марта 2012

Я бы НЕ использовал 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] -----------------------------------------------------
6 голосов
/ 24 августа 2013
#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
4 голосов
/ 05 ноября 2010

Кроме того, здесь показано, как вычислить 64-битную NSNumber, инициализированную с эпохой Unix, в миллисекундах, если вы хотите сохранить ее в CoreData. Мне это нужно для моего приложения, которое взаимодействует с системой, которая хранит даты таким образом.

  + (NSNumber*) longUnixEpoch {
      return [NSNumber numberWithLongLong:[[NSDate date] timeIntervalSince1970] * 1000];
  }
2 голосов
/ 21 июня 2018

Функции, основанные на mach_absolute_time, хороши для коротких измерений.
Но для длительных измерений важно предостеречь, что они перестают тикать, когда устройство спит.

Есть функция для получения времени с момента загрузки. Это не останавливается во время сна. Кроме того, gettimeofday не монотонен, но в моих экспериментах я всегда видел, что время загрузки изменяется при изменении системного времени, поэтому я думаю, что оно должно работать нормально.

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(&currentTime, &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 - изменяется при изменении системного времени, не монотонно
  • CFAbsoluteTimeGetCurrent() - не монотонно, может идти назад
  • CACurrentMediaTime() - перестает тикать, когда устройство спит
  • timeSinceBoot() - не спит, но может быть не монотонным
  • CLOCK_MONOTONIC - не спит, монотонный, поддерживается начиная с iOS 10
1 голос
/ 24 января 2012

Я знаю, что это старый, но даже я обнаружил, что снова прошёл мимо него, поэтому я решил представить здесь свой вариант.

Лучше всего проверить мой пост на этом блоге: Сроки в Objective-C: секундомер

По сути, я написал класс, который перестает смотреть в основном виде, но инкапсулирован так, что вам нужно только сделать следующее:

[MMStopwatchARC start:@"My Timer"];
// your work here ...
[MMStopwatchARC stop:@"My Timer"];

И в итоге вы получите:

MyApp[4090:15203]  -> Stopwatch: [My Timer] runtime: [0.029]

в журнале ...

Опять же, посмотрите мой пост немного подробнее или скачайте его здесь: MMStopwatch.zip

0 голосов
/ 17 августа 2016

Вы можете получить текущее время в миллисекундах с 1 января 1970 года, используя NSDate:

- (double)currentTimeInMilliseconds {
    NSDate *date = [NSDate date];
    return [date timeIntervalSince1970]*1000;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...