******阅读完此文,大概需要5分钟******
一、问题产生与分析
先看下产生的代码:
- (void)dealloc
{
[self.timer invalidate];
self.timer = nil;
NSLog(@"dealloc!!!!!!!");
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.timer = [NSTimer scheduledTimerWithTimeInterval:3.0f
target:self
selector:@selector(timerFire)
userInfo:nil
repeats:YES];
[self.timer fire];
}
- (void)timerFire
{
NSLog(@"fire");
}
在这段代码,你在dealloc处放置一个breakpoint,你会发现dealloc方法不会执行的。因为此时存在着一个引用循环:
每个NSTimer其实是被添加在所在线程的runloop中,而runloop对timer是一种强持有关系,看下苹果官网:
也就是说,此时的timer采取strong property的方式其实是不合理的。那么为什么Runloop要strong reference to a timer呢,首先,NSTimer的执行需要加到runloop中去。RunLoop有一个CFRunLoopTimerRef 是基于时间的触发器的类,它和 NSTimer 是toll-free bridged 的,可以混用。其包含一个时间长度和一个回调(函数指针)。当其加入到 RunLoop 时,RunLoop会注册对应的时间点,当时间点到时,RunLoop会被唤醒以执行那个回调(这就是为什么要强持有target)。
二、解决方法
1、及时invalidate()掉Timer
invalidate()的作用,苹果有描述 :
执行之后,Runloop对Timer的强引用就会remove掉,同时timer对target的强引用也会remove掉,通过CFGetRetainCount()方法查看self的引用即可知道,验证如下:
NO Repeats的Timer是会自动invalidate()的,所以,invalidate()后timer仅仅是self的一个属性变量了。
对于Repeats类型的Timer需要在合适的时机去手动invalidate()了,例如在viewDidDisappear方法中,就是一种不错的尝试。
2、不需要手动设置NSTimer为invalid 的方法:造一个假的target给NSTimer,假的target作用就是用于接受NSTimer的强引用。
具体的思路代码,如下:
[...] @implementation NSWeakTimerTarget { __weak target; SEL selector; } [...] - (void)timerDidFire:(NSTimer *)timer { if (target) { [target performeSelector:selector withObject:timer]; } else { [timer invalidate]; } } @end @implementation NSWeakTimer + (NSTimer *)scheduledTimerWithTimerInterval:(NSTimeInterval)ti target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo repeat:(BOOL)repeats { NSWeakTimerTarget *timerTarget = [[NSWeakTimerTarget alloc] init] timerTarget.target = aTarget; timerTarget.selector = aSelector; timerTarget.timer = [NSTimer scheduledTimerWithTimeInterval:ti target:timerTarget selector:@selector(timerDidFire:) userInfo: userInfo repeats: repeats]; return timerTarget.timer; } @end此法真正的target并不会被timer 持有,当真正的target为空的时候,timer会执行invalid。此法的github地址:https://github.com/ChatGame/HWWeakTimer
三、参考资料
1、https://developer.apple.com/reference/foundation/timer/1415405-invalidate?changes=latest_minor
2、http://www.jianshu.com/p/f9999b5958f8