获得iOS系统的正常运行时间,睡眠时不会暂停

我正在寻找一种方法来获得一个绝对的,不断增加的iOS系统正常运行时间。

它应该返回自设备上次重新启动以来的时间,而不会受到系统date更改的影响。

当设备睡着时(CACurrentMediaTime,[NSProcessInfo systemUptime],mach_absolute_time),或者系统date改变(sysctl / KERN_BOOTTIME)时,所有的方法都可以暂停。

有任何想法吗?

我想我已经解决了。

time()在设备处于睡眠状态时进行递增,但当然可以由操作系统或用户操作。 然而,当系统时钟改变时,内核启动时间(系统上次启动时的时间戳)也会改变,因此即使这两个值都不固定,它们之间的偏移也是如此。

#include <sys/sysctl.h> - (time_t)uptime { struct timeval boottime; int mib[2] = {CTL_KERN, KERN_BOOTTIME}; size_t size = sizeof(boottime); time_t now; time_t uptime = -1; (void)time(&now); if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 && boottime.tv_sec != 0) { uptime = now - boottime.tv_sec; } return uptime; } 

列出的所有示例都有一个竞争条件:如果时间有变化(NTP或用户修改),代码将会竞争,时钟不再是单调的。

正确的实现是:

 #include <sys/sysctl.h> static int64_t us_since_boot() { struct timeval boottime; int mib[2] = {CTL_KERN, KERN_BOOTTIME}; size_t size = sizeof(boottime); int rc = sysctl(mib, 2, &boottime, &size, NULL, 0); if (rc != 0) { return 0; } return (int64_t)boottime.tv_sec * 1000000 + (int64_t)boottime.tv_usec; } - (int64_t)us_uptime { int64_t before_now; int64_t after_now; struct timeval now; after_now = us_since_boot(); do { before_now = after_now; gettimeofday(&now, NULL); after_now = us_since_boot(); } while (after_now != before_now); return (int64_t)now.tv_sec * 1000000 + (int64_t)now.tv_usec - before_now; } 

如果需要更高的精度,则下面是已接受答案的修改版本。

 #include <sys/sysctl.h> - (NSTimeInterval)uptime { struct timeval boottime; int mib[2] = {CTL_KERN, KERN_BOOTTIME}; size_t size = sizeof(boottime); struct timeval now; struct timezone tz; gettimeofday(&now, &tz); double uptime = -1; if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 && boottime.tv_sec != 0) { uptime = now.tv_sec - boottime.tv_sec; uptime += (double)(now.tv_usec - boottime.tv_usec) / 1000000.0; } return uptime; } 

我会评论Leszek的答案,但是没有足够的代表… Leszek的解决scheme返回秒。

以下是Leszek答案的修改版本,如果有人需要毫秒的精度。

 #include <sys/sysctl.h> + (long long int)uptime { struct timeval boottime; int mib[2] = {CTL_KERN, KERN_BOOTTIME}; size_t size = sizeof(boottime); struct timeval now; struct timezone tz; gettimeofday(&now, &tz); long long int uptime = -1; if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 && boottime.tv_sec != 0) { uptime = ((long long int)(now.tv_sec - boottime.tv_sec)) * 1000; uptime += (now.tv_usec - boottime.tv_usec) / 1000; } return uptime; } 

正如其中一条评论所说, POSIX的 clock_gettime()是在iOS 10和MacOS 10.12中实现的。 当与CLOCK_MONOTONIC参数一起使用时,它似乎返回正常运行时间值。 但是,这不是由文档保证的:

对于此时钟,clock_gettime()返回的值表示自过去未指定点(例如,系统启动时间或Epoch)以来的时间量(以秒和纳秒为单位)。 系统启动时间后,这一点不会改变。

并从相应的macOS手册页摘录:

CLOCK_MONOTONIC时钟单调递增,跟踪任意点以来的时间,并在系统hibernate时继续增加。

CLOCK_MONOTONIC_RAW时钟单调递增,追踪自CLOCK_MONOTONIC之类的任意点开始的时间。 但是,这个时钟不受频率或时间调整的影响。 它不应该与其他系统时间源进行比较。

像CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC_RAW_APPROX,但读取系统在上下文切换时caching的值。 这可以更快地读取,但是会损失精确度,因为它可能会返回毫秒级的值。

CLOCK_UPTIME_RAW时钟单调递增,与CLOCK_MONOTONIC_RAW相同,但在系统hibernate时增加。 在适当的mach_timebase转换应用之后,返回值与mach_absolute_time()的结果相同。

请注意, CLOCK_MONOTONIC以微秒精度返回,而CLOCK_MONOTONIC_RAW以纳秒精度返回。

SWIFT代码:

 func uptime() -> timespec { var uptime = timespec() if 0 != clock_gettime(CLOCK_MONOTONIC_RAW, &uptime) { fatalError("Could not execute clock_gettime, errno: \(errno)") } return uptime } print(uptime()) // timespec(tv_sec: 636705, tv_nsec: 750700397) 

对于那些仍然感兴趣的在Swift中获取内核的启动时间:

 func kernelBootTime() -> timeval { var mib = [ CTL_KERN, KERN_BOOTTIME ] var bootTime = timeval() var bootTimeSize = MemoryLayout<timeval>.size if 0 != sysctl(&mib, UInt32(mib.count), &bootTime, &bootTimeSize, nil, 0) { fatalError("Could not get boot time, errno: \(errno)") } return bootTime } print(kernelBootTime()) // timeval(tv_sec: 1499259206, tv_usec: 122778) 

为什么不使用systemUptime ?

systemUptime返回从计算机重新启动以来的时间。

  • (NSTimeInterval)systemUptime返回值一个NSTimeInterval,指示自计算机重新启动以来的时间。

可用性iOS 4.0及更高版本中提供。 在NSProcessInfo.h中声明

我已经testing并certificate,至less在iOS 7.1.1的iPhone 5上,systemUptime在手机locking时不会停止。 所以任何人都不相信这可以自己testing。