版本記錄
版本號 | 時(shí)間 |
---|---|
V1.0 | 2017.08.30 |
前言
很多時(shí)候我們是需要服務(wù)器和客戶端時(shí)間的同步的,比如說活動(dòng)的倒計(jì)時(shí)碴犬,搶購等。而且做好時(shí)間同步還有一個(gè)優(yōu)勢就是防止客戶端用戶本地時(shí)間有誤差導(dǎo)致問題。感興趣的可以看上面幾篇停做。
1. iOS關(guān)于時(shí)間的處理(一)—— 有關(guān)時(shí)間的基本知識
功能要求
要做到客戶端和服務(wù)器端時(shí)間同步。
功能實(shí)現(xiàn)
1. 問題分析
我們可以按照下面的思路進(jìn)行
- 獲取服務(wù)器某一時(shí)刻
A
的時(shí)間大莫; - 記錄獲取到時(shí)刻
A
時(shí)的本地時(shí)間B
蛉腌; - 需要用到時(shí)間時(shí),獲取當(dāng)前本地時(shí)間
C
,當(dāng)C - B
作為時(shí)間間隔D
烙丛,則A + D
則是當(dāng)前服務(wù)器的時(shí)間舅巷。
這里要準(zhǔn)確做到客戶端時(shí)間和服務(wù)器時(shí)間一致,很關(guān)鍵的問題就是B和C不能受系統(tǒng)時(shí)間的影響河咽。要解決這個(gè)問題钠右,要依靠iOS的兩個(gè)接口。
- 獲取設(shè)備當(dāng)前時(shí)間
Now
库北,該值受系統(tǒng)時(shí)間影響爬舰,用戶如果修改時(shí)間,值也會隨著變化寒瓦。 - 獲取設(shè)備上次重啟的時(shí)間
BootTime
情屹,該值受系統(tǒng)時(shí)間影響,用戶如果修改時(shí)間杂腰,值也會隨著變化垃你。
由上面 iOS 提供的兩個(gè)接口,我們可以獲取本地時(shí)間 B
喂很、C
:設(shè)備自上次重啟后運(yùn)行的時(shí)間(BootTime - Now)
惜颇,該值與系統(tǒng)時(shí)間無關(guān)。利用時(shí)間差這一概念就很好的解決了時(shí)間受系統(tǒng)影響的這個(gè)問題了少辣。
2. 代碼實(shí)現(xiàn)
- 獲取當(dāng)前
Unix Time
static func now() -> Int {
var now = timeval()
var tz = timezone()
gettimeofday(&now, &tz)
return now.tv_sec
}
- 獲取設(shè)備上次重啟的
Unix Time
func boottime() -> Int {
var mid = [CTL_KERN, KERN_BOOTTIME]
var boottime = timeval()
var size = MemoryLayout.size(ofValue: boottime)
if sysctl(&mid, 2, &boottime, &size, nil, 0) != -1 {
return boottime.tv_sec
}
return 0
}
- 進(jìn)行時(shí)間校準(zhǔn)
// 接口獲取服務(wù)器時(shí)間處理
let serverTime = xxx // 獲取到的服務(wù)器時(shí)間
let runTime0 = now() - boottime() // 當(dāng)前設(shè)備運(yùn)行時(shí)間
// 需要用到時(shí)間時(shí)
let runTime1 = now() - boottime() // 當(dāng)前時(shí)刻設(shè)備運(yùn)行時(shí)間
let currentTime = serverTime + runTime1 // 當(dāng)前服務(wù)器時(shí)間
題外話
關(guān)于時(shí)間的使用凌摄,還有另外一個(gè)用途就是時(shí)間的測量,在做方法執(zhí)行時(shí)間的benchmark
的時(shí)候漓帅,我們獲取時(shí)間的方法要滿足兩個(gè)要求锨亏,一是精讀要高,而是API本身幾乎不耗CPU時(shí)間忙干。一般認(rèn)為UI線程只要阻塞16.7ms
就會出現(xiàn)掉幀現(xiàn)象器予,第一篇中提到的幾種時(shí)間的方法,其中NSDate
調(diào)用返回的精讀是0.000004 S
捐迫,也就是4微秒乾翔,CACurrentMediaTime()
返回的精讀也在微秒級別,精讀上都符合要求施戴。
下面看一下別人提供的示例代碼
int testCount = 10000;
double avgCost = 0;
for (int i = 0; i < testCount; i ++) {
NSDate* begin = [NSDate date];
NSLog(@"a meaningless log");
avgCost += -[begin timeIntervalSinceNow];
}
NSLog(@"benchmark with NSDate: %f", avgCost/testCount);
avgCost = 0;
for (int i = 0; i < testCount; i ++) {
double startTime = CACurrentMediaTime();
NSLog(@"a meaningless log");
double endTime = CACurrentMediaTime();
avgCost += (endTime - startTime);
}
NSLog(@"benchmark with CACurrentMediaTime: %f", avgCost/testCount);
下面看輸出結(jié)果
benchmark with NSDate: 0.000046
benchmark with CACurrentMediaTime: 0.000037
可以看出CACurrentMediaTime
與NSDate
代碼本身的損耗差異在幾微秒反浓,而我們做UI性能優(yōu)化的維度在毫秒級別,幾個(gè)微秒的差異完全不會影響我們最后的判斷結(jié)果赞哗。所以使用NSDate做benchmark完全是可行的勾习。
參考文章
1. iOS 客戶端與服務(wù)端做時(shí)間同步
2. iOS關(guān)于時(shí)間的處理