1. 程式人生 > IOS開發 >利用類擴充套件解決NSTimer會保留目標物件

利用類擴充套件解決NSTimer會保留目標物件

場景

在程式開發中經常會遇到計時器,比如促銷活動的倒計時,傳送簡訊驗證碼過段時間才允許第二次傳送、設定一段倒計時。

問題

這時會用到 下面這個api, scheduledTimerWithTimeInterval:target:selector:userInfo:repeats: API_AVAILABLE(ios(2.0)),這個api從ios 2.0就開始提供了,可以相容較低版本的ios系統。可是由於Timer會保留target引數,所以這個api會比較容易造成迴圈引用。想象一下有一個自定義ViewController1,裡邊有個倒計時功能,很容易通過下邊的程式碼實現:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    _timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(invokeTimer) userInfo:nil repeats:YES];
}

- (void)invokeTimer {
    NSLog(@"%@"
,self); } - (void)dealloc { [_timer invalidate]; NSLog(@"%s",__FUNCTION__); } 複製程式碼

每過一秒鐘觸發一次,觸發方法會輸出log,在dealloc中將timer置為無效。看似天衣無縫,但實際上,從自定義ViewController1返回時,並不會呼叫dealloc,原因就是上文中提到的NSTimer對target的保留。這樣就會造成ViewController1的記憶體洩漏了。

解決思路

利用像下邊這樣的類擴充套件,此時Timer的target 不再是自定義ViewController1

typedef void (^TimerHandler) (NSTimer *);

@interface NSTimer (HandleRetainTarget)

@end

@implementation NSTimer(HandleRetainTarget)

+ (NSTimer *)Eoc_timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))handler  {
    return
[NSTimer scheduledTimerWithTimeInterval:interval target:[self class] selector:@selector(repeatTimer:) userInfo:[handler copy] repeats:YES]; } + (void)repeatTimer:(NSTimer *)timer { TimerHandler handler = timer.userInfo; if (handler) { handler(timer); } } @end 複製程式碼

呼叫示例

__weak typeof (self) weakself = self;
_timer = [NSTimer Eoc_timerWithTimeInterval:1.0 repeats:YES block:^(NSTimer *timer) {
     [weakself invokeTimer];
}];
複製程式碼

Demo Git 地址

截圖

點選Button -> 點選Back -> 檢視log

!