写这篇文章也是受到上篇文章的启发,想好好测试一下NSTimer。毕竟是一个常用的知识点。以下观点也仅供参考,求各位大神多提点。
首先要说明的是:
[NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
这种匿名的方式是完全不能使用的,因为该定时器是会永远存在,并且它“看上去”强引用了target,也就是这里的self也不会释放。
推荐一个我以前的写法,估计基本都这样:
@property (nonatomic, strong) NSTimer *timer;
- (void)addTimer {
self.timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}
- (void)removeTimer {
[self.timer invalidate];
self.timer = nil;
}
经过测试,虽然这里使用strong强引用了timer,但是同样在pop控制器并且执行removeTimer
之后,控制器执行了dealloc
,并且定时器停止了。也不存在循环引用问题。
这里分析一下:为什么前面那种写法,会导致timer、self一直存在呢?我觉得并不是循环引用的问题,而是scheduledTimerWithTimeInterval
导致在runloop中,对timer、self有了强引用。如果timer执行invalidate
,则在runloop中,就会取消对timer及self的强引用了。也不经回想一下UIControl,然后addTarget
后,该控件就对target进行了强引用?!
另外苹果对NSTimer还提供了block方法:scheduledTimerWithTimeInterval: repeats: block:
、timerWithTimeInterval: repeats: block:
。
这里经过测试,只要timer执行了invalidate
,同样控制器执行了dealloc
,并且定时器停止了。
不过发现如果timer不执行invalidate
,这种方式,在block中使用了self而不是weakSelf,self将不会被释放。
我的理解是这样的:因为timer被添加进runloop了,所以肯定不会释放。而timer对block应该进行了强引用,block对其中使用了的self又进行了强引用,从而导致self不会被释放。如果使用weakSelf,timer会存在,time中的block存在但不对weakSelf强引用,所以weakSelf该释放就会释放。
// __weak typeof(self) weakSelf = self;
[NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"block - %@ - scheduledTimerWithTimeInterval", self);
// NSLog(@"block - %@ - scheduledTimerWithTimeInterval", weakSelf);
}];