如何获得精确的时间,例如在Objective-C中以毫秒为单位?

Tha*_*nks 97 iphone cocoa-touch objective-c uikit

是否有一种简单的方法可以非常精确地获得时间?

我需要计算方法调用之间的一些延迟.更具体地说,我想计算UIScrollView中滚动的速度.

Jef*_*son 127

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;
Run Code Online (Sandbox Code Playgroud)

关于timeIntervalSinceNow的文档.

有许多其他方法可以使用计算此间隔NSDate,我建议查看NSDateNSDate类参考中找到的类文档.

  • 使用NSDate来比较经过的时间是不安全的,因为系统时钟可以随时改变(由于NTP,DST转换,闰秒和许多其他原因).请改用mach_absolute_time. (7认同)
  • 实际上这对于一般用例来说足够精确. (3认同)
  • 使用NSDates计算经过的时间是否安全?在向外部时间源同步时,系统时间可以向前或向后,因此您无法信任结果.你真的想要一个单调增加的时钟,不是吗? (3认同)

Kri*_*son 40

mach_absolute_time() 可用于获得精确的测量结果.

请参阅http://developer.apple.com/qa/qa2004/qa1398.html

也可用CACurrentMediaTime(),它本质上是相同的东西,但具有更易于使用的界面.

  • 核心动画(QuartzCore.framework)还提供了一种方便的方法`CACurrentMediaTime()`,它将`mach_absolute_time()`直接转换为`double`. (8认同)

nev*_*vyn 26

请不要使用NSDate,CFAbsoluteTimeGetCurrentgettimeofday测量经过的时间.这些都依赖于系统时钟,它可以在改变任何时间,由于许多不同的原因,诸如网络时间同步(NTP)更新时钟(经常发生以调整漂移),DST调整,闰秒,等等.

这意味着,如果您正在测量下载或上传速度,您的数字会突然出现峰值或下降,与实际发生的情况无关; 你的性能测试会有奇怪的错误异常值; 并且您的手动计时器将在不正确的持续时间后触发.时间甚至可能倒退,你最终得到负增量,你最终会得到无限递归或死代码(是的,我已经完成了这两个).

使用mach_absolute_time.它测量自内核启动以来的实际秒数.它是单调增加的(永远不会倒退),并且不受日期和时间设置的影响.由于使用它很痛苦,这里有一个简单的包装器,它可以为你提供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
Run Code Online (Sandbox Code Playgroud)

  • 谢谢.那么来自QuartzCore的`CACurrentMediaTime()`呢? (2认同)

Ada*_*eld 14

CFAbsoluteTimeGetCurrent()将绝对时间作为double值返回,但我不知道它的精度是什么 - 它可能每十几毫秒更新一次,或者它可能每微秒更新一次,我不知道.

  • @JimDovey:考虑到所有其他值,我不得不说`72.89674947369`秒的值非常罕见.;) (25认同)
  • CFAbsoluteTimeGetCurrent()在OS X上调用gettimeofday(),在Windows上调用GetSystemTimeAsFileTime().这是[源代码](http://opensource.apple.com/source/CF/CF-635.19/CFDate.c). (5认同)
  • @Jim:你有一个引用,它提供亚毫秒精度(这是一个诚实的问题)?我希望每个人都能理解[准确性和精确度]之间的差异(http://en.wikipedia.org/wiki/Accuracy_and_precision). (3认同)
  • 哦,gettimeofday()是通过[mach_absolute_time()](https://developer.apple.com/library/mac/#qa/qa1398/_index.html)使用Mach纳秒定时器实现的; 这是Darwin/ARM上gettimeofday()的通用页面实现的源代码:http://opensource.apple.com/source/Libc/Libc-763.12/arm/sys/arm_commpage_gettimeofday.c (3认同)
  • 它虽然是双精度浮点值,但确实提供亚毫秒精度.值72.89674947369秒并不罕见...... (2认同)

Nat*_*mer 10

我不会使用,mach_absolute_time()因为它使用ticks(可能是正常运行时间)查询内核和处理器的组合绝对时间.

我会用什么:

CFAbsoluteTimeGetCurrent();
Run Code Online (Sandbox Code Playgroud)

此功能经过优化,可以纠正iOS和OSX软件和硬件的差异.

有点怪人

差异的商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] -----------------------------------------------------
Run Code Online (Sandbox Code Playgroud)

  • 根据CFAbsoluteTimeGetCurrent()的文档,"由于与外部时间参考同步或由于时钟的明确用户更改,系统时间可能会减少".我不明白为什么有人会想用这样的东西来测量经过的时间,如果可以倒退的话. (12认同)

gng*_*zrd 6

#define CTTimeStart() NSDate * __date = [NSDate date]
#define CTTimeEnd(MSG) NSLog(MSG " %g",[__date timeIntervalSinceNow]*-1)
Run Code Online (Sandbox Code Playgroud)

用法:

CTTimeStart();
...
CTTimeEnd(@"that was a long time:");
Run Code Online (Sandbox Code Playgroud)

输出:

2013-08-23 15:34:39.558 App-Dev[21229:907] that was a long time: .0023
Run Code Online (Sandbox Code Playgroud)


Pav*_*eev 6

基于的函数适用于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
}
Run Code Online (Sandbox Code Playgroud)

从 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
    }
}
Run Code Online (Sandbox Code Playgroud)

把它们加起来:

  • Date.timeIntervalSinceReferenceDate — 随系统时间变化而变化,而不是单调的
  • CFAbsoluteTimeGetCurrent() — 不单调,可能会倒退
  • CACurrentMediaTime() - 当设备处于睡眠状态时停止滴答
  • timeSinceBoot() — 不睡觉,但可能不是单调的
  • CLOCK_MONOTONIC — 不休眠,单调,自 iOS 10 起支持


Joh*_*ght 5

此外,这里是如何以NSNumber毫秒为单位计算使用 Unix 纪元初始化的 64 位,以防您希望将其存储在 CoreData 中。我的应用程序需要这个,它与以这种方式存储日期的系统交互。

  + (NSNumber*) longUnixEpoch {
      return [NSNumber numberWithLongLong:[[NSDate date] timeIntervalSince1970] * 1000];
  }
Run Code Online (Sandbox Code Playgroud)