之前在项目中遇见了一个问题,在子线程中如何开启NSTimer和取消NSTimer。现在总结一下,当做自己的笔记。
1.子线程中NSTimer的开启
首先在.m文件中声明两个属性一个是子线程 一个是子线程中的NSTimer。
@property (nonatomic, strong) NSThread *thread1;
@property (nonatomic, strong) NSTimer *threadTimer;
然后用GCD在全局全队中创建一个子线程并创建NSTimer。
__weak __typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(0, 0), ^{
__strong __typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf) {
strongSelf.thread1 = [NSThread currentThread];
[strongSelf.thread1 setName:@"线程A"];
strongSelf.threadTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:strongSelf selector:@selector(timerAction) userInfo:nil repeats:YES];
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
[runloop addTimer:strongSelf.threadTimer forMode:NSDefaultRunLoopMode];
[runloop run];
}
});
注意的是:在子线程中创建的NSTimer需要加入到对应线程的RunLoop中。RunLoop中常用的mode有:NSDefaultRunLoopMode、UITrackingRunLoopMode和NSRunLoopCommonModes三种模式。
NSDefaultRunLoop 默认模式
UITrackingRunLoop 界面追踪,用于scrollView拖拽 滑动
NSRunLoopCommonModes 不是一个特定的模式,只是一种标记,比较综合的一种模式(包含 前两种模式)
在NSTimer加入RunLoop后,需要将RunLoop运行起来。
2.子线程中NSTimer的关闭
之后创建一个cancelTimer的方法
- (void)cancel{
if (self.threadTimer) {
[self.threadTimer invalidate];
self.threadTimer = nil;
}
}
如果这个方法跟创建NSTimer不在同一个线程执行是无法将Timer 执行invalidate操作的。
然后现在我们需要在thread1这个线程中执行这个操作,在这里写一个方法用于在子线程中调用此方法。
- (void)cancelTimer{
if (self.threadTimer && self.thread1) {
[self performSelector:@selector(cancel) onThread:self.thread1 withObject:nil waitUntilDone:YES];
}
}
最后我们在有需要关闭Timer的地方执行此方法即可。
在这里说明一下为什么NSTimer要在同一个线程中创建和关闭。因为创建的Timer的时候已经把Timer加入到该线程对应的RunLoop中,这个RunLoop设置了这个Timer为一个事件。因此要在同一个线程中才能cancel这个Timer。
网友评论
{
[super viewDidLoad];
NSLog(@"主线程 %@", [NSThread currentThread]);
//创建并执行新的线程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(newThread) object:nil];
[thread start];
}
- (void)newThread
{
@autoreleasepool
{
//在当前Run Loop中添加timer,模式是默认的NSDefaultRunLoopMode
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(timer_callback) userInfo:nil repeats:YES];
//开始执行新线程的Run Loop
[[NSRunLoop currentRunLoop] run];
}
}
即使是使用- (void)cancel; 也能停止timer的执行.timer也是有变成nil的,这是为什么呢?
@property (nonatomic,strong) NSTimer *timer;
@property (nonatomic,strong) NSThread* timerThread;
- (void)viewDidLoad
{
[super viewDidLoad];
_timerThread = [[NSThread alloc] initWithTarget:self selector:@selector(creatTimer) object:nil];
[_timerThread start];
//监听子线程的释放(为了证明下面@autoreleasepool{}的写法可以释放我们的子线程)
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(threadfinishTask:) name:NSThreadWillExitNotification object:nil];
}
- (void)threadfinishTask:(NSNotification *)notification{
NSLog(@"==============================子线程被释放了!");
}
-(void)creatTimer
{
@autoreleasepool//释放子线程
{
_timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(dylog) userInfo:@{@"key":@"value"} repeats:true];
do
{
//效果:可以实时监测到_timer的释放,从而实时的释放线程,最终控制器也可以被释放
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
} while (_timer);
}
}
-(void)timerInvalidate
{
if (_timer)
{
if ([_timer isValid])//如果timer还在runLoop中
{
[_timer invalidate];
NSLog(@"查看invalidate的执行是否和timer的创建在同一个线程!");
}
_timer = nil;
}
}
#pragma mark - NSTimer在子线程的释放测试
-(void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self performSelector:@selector(timerInvalidate) onThread:_timerThread withObject:nil waitUntilDone:YES];
}
-(void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
NSLog(@"MultiThreadUsingVC -- dealloc!");
}