詳解iOS中多倒計(jì)時(shí)場(chǎng)景的解決方案
背景
在我們開發(fā)APP的過(guò)程中,或多或少都遇到過(guò)需要使用倒計(jì)時(shí)的場(chǎng)景,大多數(shù)應(yīng)用中的用戶登錄注冊(cè)過(guò)程中獲取驗(yàn)證碼的倒計(jì)時(shí),電商或者外賣APP中的訂單送達(dá)的倒計(jì)時(shí),以及秒殺類APP的秒殺倒計(jì)時(shí)等。對(duì)于這些需要倒計(jì)時(shí)的場(chǎng)景,通常情況下的解決方案是:在需要展示倒計(jì)時(shí)的各View模塊各自維護(hù)一個(gè)自己的倒計(jì)時(shí)Timer,通過(guò)Timer的回調(diào)和模塊本身需要的倒計(jì)時(shí)時(shí)間來(lái)更新對(duì)應(yīng)View的倒計(jì)時(shí)的顯示,再在此基礎(chǔ)上加上對(duì)應(yīng)的時(shí)間校準(zhǔn)方案,一個(gè)簡(jiǎn)單的倒計(jì)時(shí)需求就完成了。
問(wèn)題
對(duì)于APP內(nèi)倒計(jì)時(shí)的業(yè)務(wù)如果只出現(xiàn)在單一的頁(yè)面或者是少數(shù)的頁(yè)面場(chǎng)景中沒什么太大的問(wèn)題的,通常對(duì)于秒殺類APP的倒計(jì)時(shí)場(chǎng)景往往是在某個(gè)頁(yè)面或者某幾個(gè)頁(yè)面中有多個(gè)倒計(jì)時(shí)共同存在的,這種產(chǎn)品需求的技術(shù)展現(xiàn)方式可能是TableView或者CollectionView中的多個(gè)Cell,也可能是多個(gè)自定義的View模塊,如果我們此時(shí)依然使用每個(gè)Cell或者每個(gè)View模塊各自維護(hù)一個(gè)單獨(dú)的倒計(jì)時(shí)Timer,當(dāng)前APP內(nèi)就會(huì)同時(shí)存在多個(gè)定時(shí)器Timer,這對(duì)于性能來(lái)說(shuō)是存在一定程度的影響的。那么我們?cè)趺床拍芨玫慕鉀Q多倒計(jì)時(shí)場(chǎng)景的問(wèn)題呢?
解決方案
既然我們不能讓每一個(gè)顯示倒計(jì)時(shí)的View模塊各自維護(hù)一個(gè)定時(shí)器Timer,那我們就提供一個(gè)專門的模塊TimerService來(lái)提供倒計(jì)時(shí)的服務(wù),TimerService內(nèi)部負(fù)責(zé)維護(hù)唯一一個(gè)定時(shí)器,同時(shí)提供添加和移除監(jiān)聽者的接口以及監(jiān)聽者需要實(shí)現(xiàn)的協(xié)議protocol,內(nèi)部通過(guò)HashTable來(lái)存儲(chǔ)監(jiān)聽者,每次定時(shí)器回調(diào),遍歷所有監(jiān)聽者進(jìn)行回調(diào),監(jiān)聽者在不需要接收定時(shí)器回調(diào)的時(shí)候只需要從TimerService中移除即可。
TimerService.h對(duì)外提供的API和監(jiān)聽者需要實(shí)現(xiàn)的協(xié)議主要如下:
//監(jiān)聽者需要實(shí)現(xiàn)的協(xié)議 @protocol TimerListener <NSObject> @required - (void)didOnTimer:(TimerService *)timer; @end //對(duì)接提供的主要接口 + (instancetype)sharedInstance; - (void)addListener:(id<TimerListener>)listener; - (void)removeListener:(id<TimerListener>)listener;
TimerService.m的內(nèi)部主要實(shí)現(xiàn)如下:
//定時(shí)器回調(diào) - (void)onTimer { [self.map.allObjects enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { id<TimerListener> listener = obj; if([listener respondsToSelector:@selector(didOnTimer:)]){ [listener didOnTimer:self]; } }]; } #pragma mark - public - (void)addListener:(id<TimerListener>)listener { TIMER_SERVICE_LOCK(self.operationsLock) if(![self.map containsObject:listener]){ [self.map addObject:listener]; if(self.map.count > 0){ //啟動(dòng) [self startTimer]; } } TIMER_SERVICE_UNLOCK(self.operationsLock) } - (void)removeListener:(id<TimerListener>)listener { TIMER_SERVICE_LOCK(self.operationsLock) if([self.map containsObject:listener]){ [self.map removeObject:listener]; if(self.map.count == 0){ //暫停 [self stopTimer]; } } TIMER_SERVICE_UNLOCK(self.operationsLock) }
使用
需要接收定時(shí)器回調(diào)的模塊,只要實(shí)現(xiàn)TimerListener協(xié)議,在需要接收定時(shí)器回調(diào)的時(shí)把其添加到TimerService中,在業(yè)務(wù)不需要接收定時(shí)器回調(diào)的時(shí)候把其從TimerService中移除即可,這樣所有的倒計(jì)時(shí)業(yè)務(wù)只需要維護(hù)一個(gè)定時(shí)器即可搞定。
其他
當(dāng)然要很好的搞定一個(gè)倒計(jì)時(shí)還需要解決其他一些問(wèn)題,比如客戶端時(shí)間校準(zhǔn)問(wèn)題,關(guān)于這個(gè)問(wèn)題推薦細(xì)細(xì)讀一下MrPeak君的一篇文章《iOS關(guān)于時(shí)間的處理》。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
iOS 16 CocoaAsyncSocket 崩潰修復(fù)詳解
這篇文章主要為大家介紹了iOS 16 CocoaAsyncSocket 崩潰修復(fù)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01淺述iOS11 Xcode 9 按住command 單擊 恢復(fù)到從前(直接跳轉(zhuǎn)到定義)
這篇文章主要介紹了 iOS11 Xcode 9 按住command 單擊 恢復(fù)到從前(直接跳轉(zhuǎn)到定義)的相關(guān)資料,需要的朋友可以參考下2017-10-10詳解使用Xcode進(jìn)行iOS設(shè)備無(wú)線調(diào)試
這篇文章主要介紹了詳解使用Xcode進(jìn)行iOS設(shè)備無(wú)線調(diào)試,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-12-12iOS 條碼及二維碼掃描(從相冊(cè)中讀取條形碼/二維碼)及掃碼過(guò)程中遇到的坑
本文主要給大家介紹ios中從手機(jī)相冊(cè)中讀取條形碼和二維碼的問(wèn)題及解決辦法,需要的朋友參考下2017-01-01iOS CAReplicatorLayer實(shí)現(xiàn)脈沖動(dòng)畫效果
這篇文章主要介紹了iOS CAReplicatorLayer實(shí)現(xiàn)脈沖動(dòng)畫效果 ,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06