首页 文章

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

提问于
浏览
96

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

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

11 回答

  • 40

    NSDatetimeIntervalSince* 方法将返回 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;
    

    Documentation on timeIntervalSinceNow .

    还有很多其他方法可以使用 NSDate 计算这个间隔,我建议查看 NSDate 的类文档,该文档位于NSDate Class Reference中 .

  • 14

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

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

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

  • 2

    请勿使用 NSDateCFAbsoluteTimeGetCurrentgettimeofday 来测量经过的时间 . 这些都取决于系统时钟,由于许多不同的原因,系统时钟可以随时改变,例如更新时钟的网络时间同步(NTP)(经常调整漂移),DST调整,闰秒等 .

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

    使用 mach_absolute_time . 它测量自内核启动以来的实际秒数 . 它是单调增加的(永远不会倒退),并且不受日期和时间设置的影响 . 既然's a pain to work with, here'是一个简单的包装器,可以给你 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
    
  • 6

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

  • -1

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

    我会用什么:

    CFAbsoluteTimeGetCurrent();
    

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

    Something Geekier

    mach_absolute_time()AFAbsoluteTimeGetCurrent() 之差的商总是大约24000011.154871

    这是我的应用程序的日志:

    Please note that final result time is a difference in CFAbsoluteTimeGetCurrent()'s

    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] -----------------------------------------------------
    
  • 1
    #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
    
  • 25

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

    + (NSNumber*) longUnixEpoch {
          return [NSNumber numberWithLongLong:[[NSDate date] timeIntervalSince1970] * 1000];
      }
    
  • -1

    基于 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开始不支持睡眠,单调,支持

  • 4

    我知道这是一个旧的,但即使我发现自己再次徘徊,所以我想我会在这里提交自己的选择 .

    最好的办法是查看我的博文:Timing things in Objective-C: A stopwatch

    基本上,我写了一个类,它以一种非常基本的方式停止观看,但是被封装,所以你只需要做以下事情:

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

    你最终得到:

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

    在日志中......

    再次,查看我的帖子多一点或下载它:MMStopwatch.zip

  • 126

    对于那些我们需要Swift版本@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
    

    我希望这对你有帮助 .

  • 10

    您可以使用NSDate获取1970年1月1日以来的当前时间(以毫秒为单位):

    - (double)currentTimeInMilliseconds {
        NSDate *date = [NSDate date];
        return [date timeIntervalSince1970]*1000;
    }
    

相关问题