利用類擴展解決NSTimer會保留目標對象

場景

在程序開發中常常會遇到計時器, 好比促銷活動的倒計時,發送短信驗證碼過段時間才容許第二次發送、設置一段倒計時。ios

問題

這時會用到 下面這個api, scheduledTimerWithTimeInterval:target:selector:userInfo:repeats: API_AVAILABLE(ios(2.0)), 這個api從ios 2.0就開始提供了,能夠兼容較低版本的ios系統。但是因爲Timer會保留target參數,因此這個api會比較容易形成循環引用。想象一下有一個自定義ViewController1,裏邊有個倒計時功能,很容易經過下邊的代碼實現:git

- (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的內存泄漏了。github

解決思路

利用像下邊這樣的類擴展,此時Timer的target 再也不是自定義ViewController1api

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
複製代碼

調用示例bash

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

Demo Git 地址 ui

截圖

點擊Button -> 點擊Back -> 查看logspa

!
相關文章
相關標籤/搜索