iOS 中 NSTimer 使用详解

澳门新葡亰网站注册 1

前阵子在整理公司项目的时候,发现老代码在使用 NSTimer
时出现了内存泄露。然后整理了一些 NSTimer
的相关内容。比较简单,各位见笑啦。

NSTimer

澳门新葡亰网站注册 1

fire

NSTimer

我们先用 NSTimer 来做个简单的计时器,每隔5秒钟在控制台输出 Fire
。比较想当然的做法是这样的:

fire

@interface DetailViewController () 
@property (nonatomic, weak) NSTimer *timer; 
@end 
@implementation DetailViewController 
- (IBAction)fireButtonPressed:(id)sender { 
_timer = [NSTimer scheduledTimerWithTimeInterval:3.0f 
target:self 
selector:@selector(timerFire:) 
userInfo:nil 
repeats:YES]; 
[_timer fire]; 
} 
-(void)timerFire:(id)userinfo { 
NSLog(@"Fire"); 
} 
@end

我们先用 NSTimer 来做个简单的计时器,每隔5秒钟在控制台输出 Fire
。比较想当然的做法是这样的:

运行之后确实在控制台每隔3秒钟输出一次 Fire
,然而当我们从这个界面跳转到其他界面的时候却发现:控制台还在源源不断的输出着
Fire 。看来 Timer 并没有停止。

@interface DetailViewController () 
@property (nonatomic, weak) NSTimer *timer; 
@end 
@implementation DetailViewController 
- (IBAction)fireButtonPressed:(id)sender { 
_timer = [NSTimer scheduledTimerWithTimeInterval:3.0f 
target:self 
selector:@selector(timerFire:) 
userInfo:nil 
repeats:YES]; 
[_timer fire]; 
} 
-(void)timerFire:(id)userinfo { 
NSLog(@"Fire"); 
} 
@end
invalidate 

既然没有停止,那我们在 DemoViewController 的 dealloc 里加上 invalidate 的方法: 

-(void)dealloc { 
[_timer invalidate]; 
NSLog(@"%@ dealloc", NSStringFromClass([self class])); 
}

运行之后确实在控制台每隔3秒钟输出一次 Fire
,然而当我们从这个界面跳转到其他界面的时候却发现:控制台还在源源不断的输出着
Fire 。看来 Timer 并没有停止。

再次运行,还是没有停止。原因是 Timer 添加到 Runloop 的时候,会被 Runloop
强引用:

invalidate 

既然没有停止,那我们在 DemoViewController 的 dealloc 里加上 invalidate 的方法: 

-(void)dealloc { 
[_timer invalidate]; 
NSLog(@"%@ dealloc", NSStringFromClass([self class])); 
}
Note in particular that run loops maintain strong references to their timers, so you don’t have to maintain your own strong reference to a timer after you have added it to a run loop.

再次运行,还是没有停止。原因是 Timer 添加到 Runloop 的时候,会被 Runloop
强引用:

然后 Timer 又会有一个对 Target 的强引用(也就是 self ):

Note in particular that run loops maintain strong references to their timers, so you don’t have to maintain your own strong reference to a timer after you have added it to a run loop.
Target is the object to which to send the message specified by aSelector when the timer fires. The timer maintains a strong reference to target until it (the timer) is invalidated.

然后 Timer 又会有一个对 Target 的强引用(也就是 self ):

也就是说 NSTimer 强引用了 self ,导致 self
一直不能被释放掉,所以也就走不到 self 的 dealloc 里。

Target is the object to which to send the message specified by aSelector when the timer fires. The timer maintains a strong reference to target until it (the timer) is invalidated.

既然如此,那我们可以再加个 invalidate 按钮:

也就是说 NSTimer 强引用了 self ,导致 self
一直不能被释放掉,所以也就走不到 self 的 dealloc 里。

- (IBAction)invalidateButtonPressed:(id)sender { 
[_timer invalidate]; 
}

既然如此,那我们可以再加个 invalidate 按钮:

嗯这样就可以了。(在 SOF 上有人说该在 invalidate 之后执行 _timer = nil
,未能理解为什么,如果你知道原因可以告诉我:)

- (IBAction)invalidateButtonPressed:(id)sender { 
[_timer invalidate]; 
}

在 invalidate 方法的文档里还有这这样一段话:

嗯这样就可以了。(在 SOF 上有人说该在 invalidate 之后执行 _澳门新葡亰网站注册,timer = nil
,未能理解为什么,如果你知道原因可以告诉我:)

You must send this message from the thread on which the timer was installed. If you send this message from another thread, the input source associated with the timer may not be removed from its run loop, which could prevent the thread from exiting properly.

在 invalidate 方法的文档里还有这这样一段话:

NSTimer
在哪个线程创建就要在哪个线程停止,否则会导致资源不能被正确的释放。看起来各种坑还不少。

You must send this message from the thread on which the timer was installed. If you send this message from another thread, the input source associated with the timer may not be removed from its run loop, which could prevent the thread from exiting properly.

dealloc

NSTimer
在哪个线程创建就要在哪个线程停止,否则会导致资源不能被正确的释放。看起来各种坑还不少。

那么问题来了:如果我就是想让这个 NSTimer 一直输出,直到
DemoViewController 销毁了才停止,我该如何让它停止呢?

dealloc

NSTimer 被 Runloop 强引用了,如果要释放就要调用 invalidate 方法。

那么问题来了:如果我就是想让这个 NSTimer 一直输出,直到
DemoViewController 销毁了才停止,我该如何让它停止呢?

但是我想在 DemoViewController 的 dealloc 里调用 invalidate 方法,但是
self 被 NSTimer 强引用了。

NSTimer 被 Runloop 强引用了,如果要释放就要调用 invalidate 方法。

所以我还是要释放 NSTimer 先,然而不调用 invalidate 方法就不能释放它。

但是我想在 DemoViewController 的 dealloc 里调用 invalidate 方法,但是
self 被 NSTimer 强引用了。

然而你不进入到 dealloc 方法里我又不能调用 invalidate 方法。