背景
在我們開(kāi)發(fā)APP的過(guò)程中,或多或少都遇到過(guò)需要使用倒計(jì)時(shí)的場(chǎng)景,大多數(shù)應(yīng)用中的用戶登錄注冊(cè)過(guò)程中獲取驗(yàn)證碼的倒計(jì)時(shí),電商或者外賣(mài)APP中的訂單送達(dá)的倒計(jì)時(shí),以及秒殺類(lèi)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)景中沒(méi)什么太大的問(wèn)題的,通常對(duì)于秒殺類(lèi)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è)專(zhuān)門(mén)的模塊TimerService來(lái)提供倒計(jì)時(shí)的服務(wù),TimerService內(nèi)部負(fù)責(zé)維護(hù)唯一一個(gè)定時(shí)器,同時(shí)提供添加和移除監(jiān)聽(tīng)者的接口以及監(jiān)聽(tīng)者需要實(shí)現(xiàn)的協(xié)議protocol,內(nèi)部通過(guò)HashTable來(lái)存儲(chǔ)監(jiān)聽(tīng)者,每次定時(shí)器回調(diào),遍歷所有監(jiān)聽(tīng)者進(jìn)行回調(diào),監(jiān)聽(tīng)者在不需要接收定時(shí)器回調(diào)的時(shí)候只需要從TimerService中移除即可。
TimerService.h對(duì)外提供的API和監(jiān)聽(tīng)者需要實(shí)現(xiàn)的協(xié)議主要如下:
//監(jiān)聽(tīng)者需要實(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í)有所幫助,也希望大家多多支持武林網(wǎng)。
新聞熱點(diǎn)
疑難解答
圖片精選