10

我正在编写一个 Cocoa OS X (Leopard 10.5+) 最终用户程序,它使用时间戳来计算屏幕上显示的时间的统计信息。当程序使用重复的 NSTimer 运行时,时间会定期计算。 [NSDate date]用于捕获时间戳StartFinish。以秒为单位计算两个日期之间的差异是微不足道的。

如果最终用户或 ntp 更改系统时钟,则会出现问题。 [NSDate date]依赖于系统时钟,所以如果它被改变,Finish变量将相对于Start倾斜,严重地扰乱了时间计算。我的问题:

1. 如何准确计算StartFinish之间的时间,以秒为单位,即使中途更改了系统时钟?

我在想我需要一个不变的参考时间点,这样我就可以计算从那时起已经过去了多少秒。例如,系统正常运行时间。10.6 具有- (NSTimeInterval)systemUptime, 部分NSProcessInfo, 提供系统正常运行时间。但是,这不起作用,因为我的应用程序必须在 10.5 中运行。

我尝试使用 NSTimer 创建一个计时器,但这并不准确。NSTimer 有几种不同的运行模式,一次只能运行一种。NSTimer(默认)被置于默认运行模式。如果用户开始操作 UI 足够长的时间,这将进入NSEventTrackingRunLoopMode并跳过默认运行模式,这可能导致 NSTimer 触发被跳过,使其成为一种不准确的秒数计数方式。

我还考虑过创建一个单独的线程 (NSRunLoop) 来运行 NSTimer 秒计数器,使其远离 UI 交互。但我对多线程非常陌生,如果可能的话,我想远离它。此外,我不确定如果 CPU 被另一个应用程序(Photoshop 渲染大图像等)挂起,这是否能正常工作,导致我的 NSRunLoop 被搁置足够长的时间以搞砸它NS定时器。

我很感激任何帮助。:)

4

3 回答 3

5

根据驱动此代码的原因,您有两种选择:

  • 对于绝对精度,请使用mach_absolute_time(). 它将准确给出调用函数的点之间的时间间隔。
  • 但在 GUI 应用程序中,这通常是不可取的。相反,您想要开始和结束您的持续时间的事件之间的时间差。如果是这样,比较[[NSApp currentEvent] timestamp]
于 2009-10-27T00:26:17.470 回答
2

NSSystemClockDidChangeNotification好的,这是一个很长的镜头,但您可以尝试实现类似于Snow Leopard 中可用的东西。

所以请耐心等待,因为这是一个奇怪的想法,而且绝对是非确定性的。但是如果你有一个看门狗线程在你的程序运行期间运行呢?该线程将每隔 n 秒读取一次系统时间并存储它。为了争论起见,让我们把它设为 5 秒。所以每隔 5 秒,它会将之前的读数与当前系统时间进行比较。如果存在“足够大”的差异(“足够大”肯定需要大于 5,但不要太大更大,考虑到进程调度和线程优先级的不确定性),发布一个通知,说明时间发生了重大变化。您需要对构成“足够大”(或足够小,如果时钟重置为较早的时间)的值进行模糊测试,以满足您的准确性需求。

我知道这有点骇人听闻,但是除非有任何其他解决方案,否则您怎么看?可能,或类似的东西,解决你的问题?

编辑

好的,所以您修改了原始问题,说您不想使用看门狗线程,因为您是多线程的新手。我理解对做一些比你感到舒服的更高级的事情的恐惧,但这可能最终成为唯一的解决方案。在这种情况下,您可能需要阅读一些内容。=)

是的,我知道诸如 Photoshop 之类的东西会从处理器中剔除废话是一个问题。另一个(甚至更复杂)的解决方案是,而不是拥有一个看门狗线程,而是拥有一个具有最高优先级的单独看门狗进程,因此它对处理器挂钩更免疫。但同样,这变得非常复杂。

最终编辑

为了完整起见,我将把所有其他想法都放在上面,但似乎使用系统的正常运行时间也将是处理此问题的有效方法。由于[[NSProcessInfo processInfo] systemUptime]仅适用于 10.6+,因此您只需调用mach_absolute_time(). 要访问该功能,只需#include <mach/mach_time.h>. 这应该与返回的值相同NSProcessInfo

于 2009-10-26T22:52:26.320 回答
1

我想出了一种方法来使用UpTime() C 函数,在<CoreServices/CoreServices.h>. 这将返回绝对时间(特定于 CPU),它可以很容易地转换为持续时间(毫秒或纳秒)。此处的详细信息: http ://www.meandmark.com/timingpart1.html (在第 3 部分下查看 UpTime)

我无法mach_absolute_time()正常工作,可能是因为我缺乏这方面的知识,并且无法在网上找到很多关于它的文档。它似乎抓住了与 相同的时间UpTime(),但将其转换为双倍让我目瞪口呆。

[[NSApp currentEvent] timestamp]确实有效,但前提是应用程序正在接收 NSEvents。如果应用程序进入前台,它不会接收事件,并且[[NSApp currentEvent] timestamp]只会在 NSTimer 触发方法中一次又一次地返回相同的旧时间戳,直到最终用户决定再次与应用程序交互。

感谢您的所有帮助马克和迈克!你们俩肯定都把我送到了正确的方向,导致了答案。:)

于 2009-10-27T19:17:27.533 回答