【问题标题】:Getting time elapsed in Objective-C在 Objective-C 中获取时间
【发布时间】:2010-10-19 00:22:21
【问题描述】:

我需要获取两个事件之间经过的时间,例如,UIView 的出现和用户的第一反应。

如何在 Objective-C 中实现它?

【问题讨论】:

    标签: ios objective-c


    【解决方案1】:
    NSDate *start = [NSDate date];
    // do stuff...
    NSTimeInterval timeInterval = [start timeIntervalSinceNow];
    

    timeInterval 是开始和现在之间的差异,以秒为单位,精度为亚毫秒。

    【讨论】:

    • @NicolasZozol 您可以使用fabs(...) 获取浮点数的绝对值。例如。 NSTimeInterval timeInterval = fabs([start timeIntervalSinceNow]);
    • 好像timeInterval的值是负数。
    • 如果你得到一个负值 - 乘以 -1 就可以了
    • 依赖[NSDate date] 可能会导致难以跟踪错误,请参阅this answer 了解更多信息。
    • @PinkFloydRocks — 什么?负值如何合法地出现?
    【解决方案2】:

    您不应该依赖[NSDate date] 来进行计时,因为它可能会高估或低估经过的时间。甚至在某些情况下,您的计算机似乎会穿越,因为经过的时间是负数! (例如,如果时钟在计时期间向后移动。)

    根据Winter 2013 Stanford iOS course (34:00) 的“高级 iOS 手势识别”讲座中的 Aria Haghighi 所说,如果您需要准确的时间间隔,则应使用CACurrentMediaTime()

    目标-C:

    #import <QuartzCore/QuartzCore.h>
    
    CFTimeInterval startTime = CACurrentMediaTime();
    // perform some action
    CFTimeInterval elapsedTime = CACurrentMediaTime() - startTime;
    

    斯威夫特:

    let startTime = CACurrentMediaTime()
    // perform some action
    let elapsedTime = CACurrentMediaTime() - startTime
    

    原因是[NSDate date] 在服务器上同步,因此它可能导致“时间同步打嗝”,从而导致非常难以跟踪的错误。另一方面,CACurrentMediaTime() 是不会随着这些网络同步而改变的设备时间。

    您需要将add QuartzCore framework 设置为您的目标设置。

    注意:还有其他 API,例如 clock_gettime_nsec_np,也可以使用。

    【讨论】:

    • 请点赞。尽管我认为每个人都同意 NSDate 应该是您的首选,但这个建议解决了我的一个问题。在调度块内的完成块内调用[NSDate date] 时,我得到的“当前”时间与[NSDate date] 在执行到达创建块的点之前立即报告的时间相同。 CACurrentMediaTime() 解决了这个问题。
    • 我们如何使用 elapsedTime 进行显示,如 mm:ss?
    • @Cyber​​Mew:这是一个单独的问题。一些解决方案请参见How do I break down an NSTimeInterval into year, months, days, hours, minutes and seconds on iPhone?
    • 请注意,CACurrentMediaTime() 在设备进入睡眠状态时会停止滴答作响。如果您在设备与计算机断开连接的情况下进行测试,请将其锁定,然后等待约 10 分钟,您会发现 CACurrentMediaTime() 跟不上挂钟时间。
    • 添加和导入#import
    【解决方案3】:

    使用timeIntervalSinceDate 方法

    NSTimeInterval secondsElapsed = [secondDate timeIntervalSinceDate:firstDate];
    

    NSTimeInterval 只是一个double,在NSDate 中定义如下:

    typedef double NSTimeInterval;
    

    【讨论】:

      【解决方案4】:

      对于任何来这里寻找 iOS 的 getTickCount() 实现的人,这是我将各种资源放在一起后的结果。

      以前我在这段代码中有一个错误(我首先除以 1000000),这导致我的 iPhone 6 上的输出出现了一些量化(也许这在 iPhone 4/etc 上不是问题,或者我只是从未注意到它)。请注意,如果不先执行该除法,则如果时基的分子很大,则存在溢出的风险。如果有人好奇,这里有一个包含更多信息的链接:https://stackoverflow.com/a/23378064/588476

      鉴于该信息,使用Apple的功能CACurrentMediaTime可能更安全!

      我还对mach_timebase_info 调用进行了基准测试,它在我的 iPhone 6 上大约需要 19ns,因此我删除了缓存该调用输出的(非线程安全的)代码。

      #include <mach/mach.h>
      #include <mach/mach_time.h>
      
      uint64_t getTickCount(void)
      {
          mach_timebase_info_data_t sTimebaseInfo;
          uint64_t machTime = mach_absolute_time();
      
          // Convert to milliseconds
          mach_timebase_info(&sTimebaseInfo);
          machTime *= sTimebaseInfo.numer;
          machTime /= sTimebaseInfo.denom;
          machTime /= 1000000; // convert from nanoseconds to milliseconds
      
          return machTime;
      }
      

      请注意潜在的溢出风险,具体取决于时基调用的输出。我怀疑(但不知道)它可能是每个 iPhone 型号的常数。在我的 iPhone 6 上是125/3

      使用CACurrentMediaTime() 的解决方案相当简单:

      uint64_t getTickCount(void)
      {
          double ret = CACurrentMediaTime();
          return ret * 1000;
      }
      

      【讨论】:

      • 有谁知道为什么在 mach_timebase_info 调用中有一个冗余(无效)转换?
      • @SimonTillson 这是一个很好的问题 - 要么它需要使警告静音,要么我从其他地方复制它,只是没有注意到删除它。我不记得了。
      • 这不是线程安全的。 mach_timebase_info() 将在将传入的mach_timebase_info_data_t 设置为实际值之前将其重置为{0,0},如果从多个线程调用代码,这可能会导致除以零。请改用dispatch_once()(或CACurrentMediaTime,这只是马赫时间转换为秒)。
      • 谢谢@wonder.mice,我已经更新了我的答案(也发现了一个关于量化的问题,我责怪我比我小 6 岁......)
      【解决方案5】:

      对于精确的时间测量(如 GetTickCount),还可以查看 mach_absolute_time 和此 Apple 问答:http://developer.apple.com/qa/qa2004/qa1398.html

      【讨论】:

        【解决方案6】:

        使用 NSDate 类的 timeIntervalSince1970 函数,如下所示:

        double start = [startDate timeIntervalSince1970];
        double end = [endDate timeIntervalSince1970];
        double difference = end - start;
        

        基本上,这是我用来比较 2 个不同日期之间的秒数差异的方法。也检查这个链接here

        【讨论】:

          【解决方案7】:

          其他答案是正确的(带有警告*)。我添加这个答案只是为了展示一个示例用法:

          - (void)getYourAffairsInOrder
          {
              NSDate* methodStart = [NSDate date];  // Capture start time.
          
              // … Do some work …
          
              NSLog(@"DEBUG Method %s ran. Elapsed: %f seconds.", __func__, -([methodStart timeIntervalSinceNow]));  // Calculate and report elapsed time.
          }
          

          在调试器控制台上,您会看到如下内容:

          DEBUG Method '-[XMAppDelegate getYourAffairsInOrder]' ran. Elapsed: 0.033827 seconds.
          

          *警告:正如其他人提到的,使用NSDate 计算经过的时间仅用于临时目的。一个这样的目的可能是常见的测试,粗略的分析,你只是想大致了解一种方法需要多长时间。

          风险在于设备时钟的当前时间设置可能会因为网络时钟同步而随时更改。所以NSDate时间可以随时向前或向后跳跃。

          【讨论】:

            猜你喜欢
            • 2011-12-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-12-15
            • 2011-11-17
            • 1970-01-01
            • 1970-01-01
            • 2013-02-02
            相关资源
            最近更新 更多