標籤:
object-c定時器
object-c定時器會自己主動retain當前的使用者,假設不注意調用invalidate,則非常easy引起循環參考導致記憶體泄露。以下的思路提供了一套還算可行的解決方式。
舉例:
常常在viewController中有可能有自己主動重新整理介面的需求。 擷取資料失敗後。每隔10秒自己主動重新整理又一次擷取資料,這個時候使用NSTimer是一個非常方便的事情。普通情況下直接建立一個NSTimer的repeat對象,然後實現相應的timerFireMethod方法。 當使用者主動點擊返回button時候,此介面應該被釋放。可是因為NSTimer retain了當前的viewController,導致介面記憶體泄露。 你可能會說在dealloc中調用invalidate,可是必須明確dealloc根本就不會調用,當然viewDidDisappear也一樣不會被調用。
前一段時間看了effective object-c,學習了一種非常好的思想,現分享出來。
給NSTimer加入一個類別,使用block的方式傳遞timerFireMethod。代碼例如以下:
@implementation NSTimer(LPBLocks)+(NSTimer*) lpScheduleTimerWithTimerInternal:(NSTimeInterval)interval block:(void(^)())block repeats:(BOOL)repeats{ return [self scheduledTimerWithTimeInterval:interval target:self selector:@selector(lpTimerBlockInvoke:) userInfo:[block copy] repeats:repeats];}+(void)lpTimerBlockInvoke:(NSTimer*)timer{ void(^block)() = timer.userInfo; if(block){ block(); }}@end
這個scheduledTimer方法也會retain target,可是因為這是一個類方法。它保留的是類對象,因此也就不會有什麼問題。
它傳入要啟動並執行block, 然後在回呼函數中通過userInfo得到block,並運行。
改進:
這個已經是一個非常大的改進了。我們能夠在代碼中放心的傳入block代碼。只是細緻思考一下。假設在block中引入了viewController的成員,並且timer又作為成員變數存在於viewController中。
比如例如以下的代碼:
@interface LPNextViewController (){ NSTimer* refreshTimer;}
這樣viewController和refreshTimer又陷入了循環參考的邏輯圈裡。當然能夠在block中使用weak_self的方式避免循環參考,可是寫起代碼來總是有些不順手。並且還必需要外部使用者顯式的進行。
於是非常easy想到。應該封裝到一個專門的LPTimer類中。它負責持有NSTimer。同一時候NSTimer的block使用LPTimer的weak版本號碼。
@interface LPTimer (){ NSTimer* _pollTimer; //timer selector __weak id _weak_target; SEL _selector; id _userInfo;}@end
-(void)scheduleTimerWithTimerInternal:(NSTimeInterval)interval target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)repeats{ __weak id weak_self = self; _weak_target = target; _selector = aSelector; _userInfo = userInfo; //借用第一個版本號碼的block思想 //使用了第二層間接,調用_weak_target的aSelector方法。 //這樣能夠把stopTimer給封裝進去。外部不須要管理timer的stop。 _pollTimer = [NSTimer lpScheduleTimerWithTimerInternal:1 block:^{ [weak_self doTimer]; } repeats:repeats];}
上面的代碼LPTimer持有NSTimer對象。而NSTimer啟動並執行block使用的是weak_self。
它在timer觸發的時候調用自身的doTimer方法。在doTimer中負責將方法傳遞給外部的使用者。
-(void)doTimer{ if ([_weak_target respondsToSelector:_selector]) { [_weak_target performSelector:_selector withObject:self]; } else{ DLog(@"WARNNING: unknown selector"); }}
_weak_target是外部的使用者。 外部的使用者能夠將LPTimer看成是一個普通的對象即可,持有它也不會有什麼問題。 LPTimer保留一個弱引用指向外部的使用者。在時間到timer觸發的時候,會先調到NStimer的block中。然後傳遞到LPTimer的doTimer中。然後調用到_weak_target的selector中。
必須注意釋放NStimer對象,在LPTimer釋放的時候調用NSTimer的invalidate方法。
-(void)stopTimer{ DLog(@""); [_pollTimer invalidate];}-(void)dealloc{ [self stopTimer]; DLog(@"");}
事實上。使用者都是使用的LPTimer類,那麼應該讓LPTimer表現的和NSTimer的行為一模一樣, 使用組合方式的適配器模式就能夠輕鬆搞定。
總結:
主要的思想就是NSTimer會retain一個對象,如今讓它retain類對象。
當時候到來進行觸發的時候,由NSTimer類對象觸發到Block中。繼而觸發到外部的LPTimer普通對象中。
在普通對象中我們就能夠自由的進行處理了。使用weak_target使LPTimer弱引用外部使用者,斷開外部使用者與LPTimer的關聯。
使用weak_self斷開LPTimer與NStimer的迴圈關聯。 個人覺得還算不錯的思想, 有須要的歡迎討論。
object-c編程tips-timer