计时器会保留目标对象,等到自身“失效”时再释放此对象。调用invalidate方法可令计时器失效;执行完毕相关任务后,一次性的计时器也会失效。开发者若将计时器设置成重复执行模式,那么必须自己调用invalidate方法,才能令其停止。
由于计时器会保留其目标对象,所以反复执行任务通常会导致应用程序出问题。也就是说,设置成重复执行模式的那种计时器,很容易引入“保留环”。如果此环能在某一时刻打破,那就不会出什么问题。
然而想要打破保留环,只能改变实例变量或者令计时器失效。
[_pollTimer invalidate];
_pollTimer = nil;
要么令系统将此实例回收,只有这样才能打破保留环。
但是有些时候 你是想控制器销毁时候去销毁定时器的,你肯定会说,那我们在dealloc里执行[invalidate]不就好了。好了,如果你说这句话,说明你没有理解NSTimer的工作了。
你可以回头看看我写的
“当我们新建一个timer时候,timer都会去对target对象持有一份,以保证timer还活着的时候,控制器不会被销毁”
也就是说 只要timer活着,控制器就不会被销毁,dealloc也就不会被执行。所以在dealloc里调用indalidate方法是没有效果的。我们暂且把这种现象称为NSTimer的保留环问题。
2)那我们如何去解决这个问题呢,当然就是想要timer不要对控制器的方法强持有。
解决的方法是在NSTimer 的基础上,建一个分类(Category) 并实现一个类方法,这里我们叫做:
+(NSTimer *)kscheduledTimerWithTimeInterval:(NSTimeInterval)interval block:(void (^)())block repeats:(BOOL)repeats;
//接口部分
@interface NSTimer (CLBlockSupport)
+(NSTimer *)kscheduledTimerWithTimeInterval:(NSTimeInterval)interval block:(void(^)())block repeats:(BOOL)repeats;
@end
//实现部分
@implementation NSTimer (CLBlockSupport)
+(NSTimer *)kscheduledTimerWithTimeInterval:(NSTimeInterval)interval block:(void (^)())block repeats:(BOOL)repeats
{
return [self scheduledTimerWithTimeInterval:interval target:self selector:@selector(clblockInvoke:)userInfo:[block copy]repeats:repeats];
}
+(void)clblockInvoke:(NSTimer*)timer
{
void(^block)()=timer.userInfo;
if (block)
{
block();
}
}
定义一个NSTimer的类别,在类别中定义一个类方法。类方法有一个类型为块的参数(定义的块位于栈上,为了防止块被释放,需要调用copy方法,将块移到堆上)。
使用这个类别的方式如下:
__weak ViewController *weakSelf = self;
_timer = [NSTimer kscheduledTimerWithTimeInterval:5.0 block:^{ __strong ViewController *strongSelf = weakSelf;
[strongSelf startCounting]; } repeats:YES];
使用这种方案就可以防止NSTimer对类的保留,从而打破了循环引用的产生。__strong ViewController *strongSelf = weakSelf
主要是为了防止执行块的代码时,类被释放了。
在类的dealloc方法中,记得调用[_timer invalidate]。