相信iOS開發(fā)過程中锥余,肯定大多數(shù)人都知道Timer的釋放不掉問題,但是否認(rèn)真考慮過其中釋放不掉的原因囤采?
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerRun:) userInfo:nil repeats:YES];
NSTimer不釋放原因
其中考慮較多的一個版本是:controller與Timer循環(huán)引用握玛,導(dǎo)致彼此不能釋放篮迎,但真正原因真是這樣嗎?
驗(yàn)證方式:
1.把timer寫為局部變量,避免controller強(qiáng)引用timer
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerRun:) userInfo:nil repeats:YES];
然而锰霜,并不能釋放筹误。
2.我們來看看引用情況
打開xcode的Debug Memory Graph工具,在這里
能看到內(nèi)存引用情況
驗(yàn)證結(jié)果:只有timer單向的指向target锈遥,target并未指向timer纫事。而timer的不釋放原因在于runloop的強(qiáng)引用,見官方文檔:
錯誤解決辦法
1.dealloc中調(diào)用invalidate
- (void)dealloc {
if(_timer) {
[_timer invalidate];
}
}
這是不行的所灸!
如果_timer的target是self,會對self進(jìn)行強(qiáng)引用(即使傳入weakSelf也是不行的)丽惶,導(dǎo)致self不能釋放,也就不會走到dealloc方法里爬立。
2.viewWillDisappear中invalidate
這種方式是可以釋放掉的钾唬。
但如果我只是想在離開此頁時要釋放阱持,進(jìn)入下一頁時不要釋放筋夏,場景就不適用了亏钩。
正確解決辦法
添加一個NSTimer類的擴(kuò)展砖第,把target指給[NSTimer class]摧找,事件由加方法接收估脆,然后把事件通過block傳遞出來件相。
@interface NSTimer (block)
+ (instancetype)repeatWithInterval:(NSTimeInterval)interval block:(void(^)(NSTimer *timer))block;
@end
@implementation NSTimer (block)
+ (instancetype)repeatWithInterval:(NSTimeInterval)interval block:(void(^)(NSTimer *timer))block {
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:interval target:self selector:@selector(trigger:) userInfo:[block copy] repeats:YES];
return timer;
}
+ (void)trigger:(NSTimer *)timer {
void(^block)(NSTimer *timer) = [timer userInfo];
if (block) {
block(timer);
}
}
@end
巧妙點(diǎn)在于把block作為timer的userInfo傳遞進(jìn)入trigger:方法韵卤,避免了在本類中再添加個參數(shù)記錄block檩坚。
使用示例
@interface CAAnimationViewController ()
@property (nonatomic, weak) NSTimer *timer;
@end
@implementation CAAnimationViewController
- (void)viewDidLoad {
kWeakSelf
self.timer = [NSTimer repeatWithInterval:1 block:^(NSTimer *timer) {
//收到timer回調(diào)
[weakSelf dosomething];
}];
}
- (void)dealloc {
[_timer invalidate];
}
@end