在程序开发中常常会遇到计时器, 好比促销活动的倒计时,发送短信验证码过段时间才容许第二次发送、设置一段倒计时。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
![]() |
![]() |
!
![]() |
---|