在iOS開發中首頁展示廣告欄,并定時驅動的展示效果很常見,就目前我經手的項目來看,多數有這種展示,這種是需要用定時器驅動的,然基本沒有人考慮到NSTimer使用造成的循環引用問題,特別是對于一些需要切換框架的App,頻繁的切換框架,造成頻繁的創建新框架,原框架由于和NSTimer相互引用無法銷毀,造成內存增長,閑話不多說,進入正題:
一般情況下創建定時器的方法:
- (NSTimer *)verifyTimer{
if (!_verifyTimer) {//立刻執行
_verifyTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(verifyTimerAction:) userInfo:nil repeats:YES];
}
return _verifyTimer;
}
self.timer = [NSTimer timerWithTimeInterval:3.0f target:self selector:@selector(timer:) userInfo:nil repeats:YES]
//加入runloop后立即執行
[[NSRunLoop currentRunLoop] addTimer:self.bannerTimer forMode:NSRunLoopCommonModes];
在這里不對每個參數過多介紹,就上邊的兩種創建方式看,都存在循環引用的情況
一般我們在使用NSTimer時都會把它當做一個屬性來使用,NSTimer被self引用,在重復調用方法時,將self作為target,這時NSTimer會獲取保留self,此時就會造成循環引用使self和NSTimer都不能銷毀,當然我們可以在銷毀self之前使用以下兩句銷毀NSTimer
[self.verifyTimer invalidate];
self.verifyTimer = nil;
然不是所有開發者都這么注意此處的循環引用問題,廢話不多說,直接上代碼,讓我們對使用方法做一下處理
__weak __typeof(&*self)ws = self;
if (self.bannerTimer == nil) {//此處必須加判斷,若是重復創建加入runloop,則會同時進行若干個timer,廣告切換則會加快
self.bannerTimer = [NSTimer block_TimerWithTimeInterval:3.0f block:^{
[ws nextPageAction];
} repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.bannerTimer forMode:NSRunLoopCommonModes];
}
使用上述方法需要創建NSTimer的分類
#import <Foundation/Foundation.h>
@interface NSTimer (TimerBlockSuport)
/**
分類解決NSTimer在使用時造成的循環引用的問題
@param interval 間隔時間
@param block 回調
@param reqeats 是否立刻執行
@return 返回NSTimer實體
*/
+ (NSTimer *)block_TimerWithTimeInterval:(NSTimeInterval)interval block:(void(^)())block repeats:(BOOL)reqeats;
@end
#import "NSTimer+TimerBlockSuport.h"
@implementation NSTimer (TimerBlockSuport)
+ (NSTimer *)block_TimerWithTimeInterval:(NSTimeInterval)interval block:(void (^)())block repeats:(BOOL)reqeats{
return [self timerWithTimeInterval:interval target:self selector:@selector(blockinvoke:) userInfo:[block copy] repeats:reqeats];
}
+ (void)blockinvoke:(NSTimer *)timer{
void (^block)() = timer.userInfo;
if (block) {
block();
}
}
上述創建方式我們方法的調用者是NSTImer自己,只是NSTimer捕獲了參數block,若是block中也是用self調用方法的話,也會造成循環引用,我們這里將self弱引用,這樣NSTimer不會捕獲self的強引用,巧妙的避開了循環引用。
小坑:
在使用時遇到一個小問題,在使用定時器時,我沒有判斷屬性NSTimer是否為nil,導致多次對屬性NSTImer賦值,造成runloop中有多個調用同一個方法的NSTimer在跑,導致重復執行時間間隔變小的問題,在這提醒一下,使用時切記判斷是否為nil