多個(gè)cell中展示倒計(jì)時(shí),本地和服務(wù)器時(shí)間差異解決方案

轉(zhuǎn)載注明出處:http://m.itdecent.cn/p/97ec4b8f018c

本文借鑒了IGListKit中多cell通知方案

Demo下載

公司需要做限時(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)單,建議可以看看

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1.自定義控件 a.繼承某個(gè)控件 b.重寫initWithFrame方法可以設(shè)置一些它的屬性 c.在layouts...
    圍繞的城閱讀 3,714評(píng)論 2 4
  • 最近這幾天研究了下Runloop,下面就來分享一下心得(有不好的地方請(qǐng)幫忙指出來,共同進(jìn)步,謝謝!!!) 一:前...
    Small_Potato閱讀 1,137評(píng)論 1 11
  • 目錄 Runloop RunLoop 與線程 個(gè)人理解總結(jié) 應(yīng)用場(chǎng)景 1. 什么是RunLoop 基本作用 保持程...
    Ryan___閱讀 1,624評(píng)論 0 13
  • 第三方框架SDWebImage (1)SDWebImage基本使用01 設(shè)置imageView的圖片[cell.i...
    _Weak閱讀 758評(píng)論 0 0
  • 正式上班第二天,想說的太多了,沒邏輯了,哈哈,首先,新年快樂,額,不喜歡在城市過年,沒啥意思,起初,玩得...
    1大太陽(yáng)閱讀 343評(píng)論 0 1

友情鏈接更多精彩內(nèi)容