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

时间:2009-05-20 18:05:43

标签: iphone objective-c cocoa-touch uikit

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

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

11 个答案:

答案 0 :(得分:126)

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 Class Reference NSDate的类文档

答案 1 :(得分:40)

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

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

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

答案 2 :(得分:26)

请勿使用NSDateCFAbsoluteTimeGetCurrentgettimeofday来衡量已用时间。这些都取决于系统时钟,由于许多不同的原因,它可以在任何时间发生变化,例如更新时钟的网络时间同步(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

答案 3 :(得分:14)

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

答案 4 :(得分:10)

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

我会用什么:

CFAbsoluteTimeGetCurrent();

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

Gething 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] -----------------------------------------------------

答案 5 :(得分:6)

#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

答案 6 :(得分:4)

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

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

答案 7 :(得分:2)

基于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以来不支持单调睡眠

答案 8 :(得分:1)

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

最好的办法是查看我的博文: 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

答案 9 :(得分:0)

您可以使用NSDate获取自1970年1月1日以来的毫秒当前时间:

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

答案 10 :(得分:-1)

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

我希望这对你有所帮助。