iOS中多倒計時場景的解決方案
背景
在我們開發APP的過程中,或多或少都遇到過需要使用倒計時的場景,大多數應用中的使用者登入註冊過程中獲取驗證碼的倒計時,電商或者外賣APP中的訂單送達的倒計時,以及秒殺類APP的秒殺倒計時等。對於這些需要倒計時的場景,通常情況下的解決方案是:在需要展示倒計時的各View模組各自維護一個自己的倒計時Timer,通過Timer的回撥和模組本身需要的倒計時時間來更新對應View的倒計時的顯示,再在此基礎上加上對應的時間校準方案,一個簡單的倒計時需求就完成了。
問題
對於APP內倒計時的業務如果只出現在單一的頁面或者是少數的頁面場景中沒什麼太大的問題的,通常對於秒殺類APP的倒計時場景往往是在某個頁面或者某幾個頁面中有多個倒計時共同存在的,這種產品需求的技術展現方式可能是TableView或者CollectionView中的多個Cell,也可能是多個自定義的View模組,如果我們此時依然使用每個Cell或者每個View模組各自維護一個單獨的倒計時Timer,當前APP內就會同時存在多個定時器Timer,這對於效能來說是存在一定程度的影響的。那麼我們怎麼才能更好的解決多倒計時場景的問題呢?
解決方案
既然我們不能讓每一個顯示倒計時的View模組各自維護一個定時器Timer,那我們就提供一個專門的模組TimerService來提供倒計時的服務,TimerService內部負責維護唯一一個定時器,同時提供新增和移除監聽者的介面以及監聽者需要實現的協議protocol,內部通過HashTable來儲存監聽者,每次定時器回撥,遍歷所有監聽者進行回撥,監聽者在不需要接收定時器回撥的時候只需要從TimerService中移除即可。
TimerService.h對外提供的API和監聽者需要實現的協議主要如下:
//監聽者需要實現的協議
@protocol TimerListener <NSObject >
@required
- (void)didOnTimer:(TimerService *)timer;
@end
//對接提供的主要介面
+ (instancetype)sharedInstance;
- (void)addListener:(id<TimerListener>)listener;
- (void)removeListener:(id<TimerListener>)listener;複製程式碼
TimerService.m的內部主要實現如下:
//定時器回撥
- (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){
//啟動
[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)
}複製程式碼
使用
需要接收定時器回撥的模組,只要實現TimerListener協議,在需要接收定時器回撥的時把其新增到TimerService中,在業務不需要接收定時器回撥的時候把其從TimerService中移除即可,這樣所有的倒計時業務只需要維護一個定時器即可搞定。
其他
當然要很好的搞定一個倒計時還需要解決其他一些問題,比如客戶端時間校準問題,關於這個問題推薦細細讀一下MrPeak君的一篇文章《iOS關於時間的處理》。