轉(zhuǎn)載注明出處:http://m.itdecent.cn/p/97ec4b8f018c
本文借鑒了IGListKit中多cell通知方案
公司需要做限時(shí)搶購(gòu)的業(yè)務(wù),這里面有兩個(gè)需求點(diǎn):
1.在多個(gè)cell中顯示倒計(jì)時(shí)
在每個(gè)cell中添加定時(shí)器是不現(xiàn)實(shí)的,必定會(huì)增加許多性能開銷,所以肯定是使用一個(gè)定時(shí)器,關(guān)鍵在于如何通知到cell刷新UI
2.本地時(shí)間可能和服務(wù)器時(shí)間存在誤差
有的手機(jī)可能時(shí)間沒有和網(wǎng)絡(luò)同步,或者用戶故意調(diào)整了時(shí)間,所以本地時(shí)間存在錯(cuò)誤的可能,所以就定下使用服務(wù)器時(shí)間

1.在多個(gè)cell中顯示倒計(jì)時(shí)
思路是這樣的:將需要接收定時(shí)器通知的對(duì)象注冊(cè)到定時(shí)器單例中,存放在數(shù)組里面,當(dāng)定時(shí)器更新的時(shí)候遍歷數(shù)組回調(diào)通知
定時(shí)器的創(chuàng)建
注意:默認(rèn)暫停定時(shí)器,定時(shí)器默認(rèn)是加載到當(dāng)前runloop中的,在進(jìn)行UI界面操作比如滑動(dòng)列表時(shí),由于在main runloop中NSTimer是同步交付的被“阻塞”,就會(huì)導(dǎo)致NSTimer計(jì)時(shí)出現(xiàn)延誤
解決這種延誤的方法,一種是在子線程中進(jìn)行NSTimer的操作,在主線程中修改UI界面顯示操作結(jié)果;另一種是仍然在主線程中進(jìn)行NSTimer操作,但是將NSTimer實(shí)例加到main runloop的特定mode(模式)中。避免被復(fù)雜運(yùn)算操作或者UI界面刷新所干擾。
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: {[unowned self] (_) in
self.onTimer()
})
//下面這種方法要求onTimer是@objc
//Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(onTimer), userInfo: nil, repeats: true)
注冊(cè)通知
這里使用NSHashTable存放注冊(cè)對(duì)象的數(shù)組,可以防止循環(huán)引用注冊(cè)對(duì)象釋放不掉
swift的protocol是一個(gè)很好的東西,這樣可以更好的規(guī)范誰(shuí)可以注冊(cè)通知
@objc
protocol TimerListener: class {
func didOnTimer(announcer: YZTimerUtil, timeInterval: TimeInterval)
}
private let map: NSHashTable<TimerListener> = NSHashTable<TimerListener>.weakObjects()
何時(shí)注冊(cè)刪除通知
一開始是在willDisplay、didEndDisplaying方法中進(jìn)行通知注冊(cè)的,后來發(fā)現(xiàn)沒有必要,因?yàn)閏ell創(chuàng)建后就是要接收通知的willDisplay、didEndDisplaying中還要進(jìn)行cell類型的判斷,所以就改為cell- init/deinit中
deinit {
YZTimerUtil.sharedInstance.removeListener(listener: self)
}
//這里cell使用的是SB
override func awakeFromNib() {
super.awakeFromNib()
YZTimerUtil.sharedInstance.addListener(listener: self)
}
2.本地時(shí)間可能和服務(wù)器時(shí)間存在誤差
這里看項(xiàng)目需求吧,如果項(xiàng)目對(duì)時(shí)間要求沒有那么嚴(yán)格,不做服務(wù)器時(shí)間對(duì)比也行,反正服務(wù)器那邊會(huì)進(jìn)行判斷的,有些對(duì)時(shí)間要求嚴(yán)格的肯定是要做對(duì)比的,比如手機(jī)手令的動(dòng)態(tài)碼
我的思路是在定時(shí)器初始化的時(shí)候進(jìn)行網(wǎng)絡(luò)請(qǐng)求,拿到服務(wù)器的當(dāng)前時(shí)間,然后計(jì)算本地和服務(wù)器時(shí)間的差值,后面就用這個(gè)差值進(jìn)行計(jì)算。當(dāng)然,受網(wǎng)絡(luò)狀態(tài)的影響,這個(gè)時(shí)間可能也不是準(zhǔn)確的時(shí)間,但是這個(gè)時(shí)間誤差會(huì)在一個(gè)可控范圍內(nèi),為了精確時(shí)間差,可以每隔一段時(shí)間就校準(zhǔn)一次,如果要更精準(zhǔn)的,可以通過請(qǐng)求的requestTime/responseTime進(jìn)行算法計(jì)算
/// 從服務(wù)器請(qǐng)求最新的時(shí)間,簡(jiǎn)單示例
func resetServerTime() {
// 從服務(wù)器請(qǐng)求最新的時(shí)間
// 。。。
var success = true
if success {
// 請(qǐng)求成功
serverTimeInterval = 0
} else {
// 如果請(qǐng)求失敗,隔一段時(shí)間再請(qǐng)求一次
perform(#selector(resetServerTime), with: nil, afterDelay: rloadTimeInterval)
}
}
demo里面的代碼很詳細(xì),也很簡(jiǎn)單,建議可以看看