我正在尋找一種方法來獲得絕對的,始終增加的iOS系統正常運行時間。獲得iOS系統正常運行時間,在睡眠時不會暫停
它應該返回設備上次重新啓動以來的時間,並且不會受到系統日期更改的影響。當設備處於睡眠狀態
所有我能找到任一方法暫停(CACurrentMediaTime,[NSProcessInfo systemUptime],mach_absolute_time)或改變當系統日期改變(sysctl的/ KERN_BOOTTIME)。
任何想法?
我正在尋找一種方法來獲得絕對的,始終增加的iOS系統正常運行時間。獲得iOS系統正常運行時間,在睡眠時不會暫停
它應該返回設備上次重新啓動以來的時間,並且不會受到系統日期更改的影響。當設備處於睡眠狀態
所有我能找到任一方法暫停(CACurrentMediaTime,[NSProcessInfo systemUptime],mach_absolute_time)或改變當系統日期改變(sysctl的/ KERN_BOOTTIME)。
任何想法?
我想我已經搞清楚了。
時間()在設備處於睡眠狀態時進行遞增,但當然可以由操作系統或用戶操作。但是,系統時鐘更改時,內核boottime(系統上次啓動時的時間戳)也會更改,因此即使這兩個值都不固定,它們之間的偏移量也是如此。
#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;
}
爲什麼不使用systemUptime?
systemUptime 返回自計算機重新啓動以來的時間。
可用性 適用於iOS 4.0及更高版本。 宣佈 NSProcessInfo.h
我已經測試和證明,至少在iPhone 5與iOS 7.1.1,systemUptime時不你的手機被鎖定停止。所以任何人都不相信這可以自己測試。
在我的iOS 7.1.x測試中。當設備被鎖定並且屏幕關閉時,它不會停止。 –
它真的受到睡眠/清醒的影響。也許你用防止睡眠的連接電纜測試它。或者其他原因阻止你的設備在測試時睡覺... – Speakus
是的,我重新測試,它停下來,對不起。 –
如果需要更高的精度,則下面是已接受答案的修改版本。
#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;
}
有列在所有例子中的競爭條件:如果有一個時間的變化(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;
}
我測試過這一點,它看起來像一個魅力的工作。我找不到任何缺陷。謝謝!爲了記錄,最後還是有一個OS10以上的非尷尬解決方案。 clock_gettime API最終可用,並且使用CLOCK_MONOTONIC應支持單調定時器的要求,該定時器在壁鐘速率下增加並具有相當的準確性。 –
除了函數名稱'us_since_boot',這很棒。這只是啓動時間,而不是從啓動開始經過的時間。 –
注意,有可能溢出做'boottime.tv_sec * 1000000'或者當'now.tv_sec * 1000000'(在32位平臺上很容易趕上)。爲了避免這個問題,你必須做一個直接的演員。 – d12frosted
我對萊謝克的回答發表評論,但沒有足夠的代表...萊謝克的解決方案返回秒。
以下是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'sclock_gettime()
在iOS的10和MacOS 10.12實施。當與CLOCK_MONOTONIC
參數一起使用時,它似乎會返回正常運行時間值。然而,這不是由文檔保證:
對於該時鐘,由clock_gettime()返回的值表示,因爲在過去的未指定的點 的 量的時間(以秒和納秒)(例如,系統啓動時間或Epoch)。系統啓動時間後,這個點不會改變。
而且從相應的MacOS手冊頁的摘錄:
CLOCK_MONOTONIC時鐘單調遞增,跟蹤時間,因爲一個任意點,並會繼續遞增,而該系統是睡着了。
CLOCK_MONOTONIC_RAW時鐘單調遞增,跟蹤時間,因爲像CLOCK_MONOTONIC的任意點。但是,此時鐘不受頻率或時間調整的影響。它不應該與其他系統時間源進行比較。
CLOCK_MONOTONIC_RAW_APPROX像CLOCK_MONOTONIC_RAW,但在讀出上下文 開關由系統高速緩存的值。這可以更快地讀取,但由於它可能會返回毫秒級的值,因此會導致精確度的降低。
CLOCK_UPTIME_RAW時鐘單調遞增,以相同的方式作爲CLOCK_MONOTONIC_RAW,但不增量在系統處於睡眠狀態。在適當的mach_timebase轉換應用之後,返回的值與mach_absolute_time()的結果相同。
請注意,CLOCK_MONOTONIC
以微秒精度返回,而CLOCK_MONOTONIC_RAW
以納秒精度返回。
斯威夫特代碼:
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)
對於那些通過獲取內核啓動時間斯威夫特仍然有興趣:
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)
幹得好伴侶。在此之前,我將不得不去找一個服務器日期/本地日期偏移比較......非常感謝。 – Magoo
我認爲這對模擬器不起作用。 – mkeremkeskin
這是正確的。 –