在程序開發中常常會遇到計時器, 好比促銷活動的倒計時,發送短信驗證碼過段時間才容許第二次發送、設置一段倒計時。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
![]() |
![]() |
!
![]() |
---|