iOS Runloop 线程保活及坑
Thread *th = [[Thread alloc] initWithTarget:self selector:@selector(testThread) object:nil];
[th start];
- (void)testThread{
NSLog(@"test thread");
}
@implementation Thread
-(void)dealloc{
NSLog(@"NSThread dealoc");
}
@end
上面的代码, Thread 这个类继承自 NSThread ,然后重写了 dealloc 方法,目的是为了看一下这个类是否在执行完任务后被释放,我们看下打印。
2020-02-19 16:05:51.150818+0800 blogTest[7713:410122] test thread
2020-02-19 16:05:51.151482+0800 blogTest[7713:410122] NSThread dealoc
从打印结果可以看出,我们的 thread 在执行完testThread 方法之后就 dealloc 了,那我们现在要做的就是让这个线程保活,不让他 dealloc。
我们的目的是让这个线程,有事做的时候活跃起来,没有事做的时候就休眠
- (void)testThread{
NSLog(@"test thread");
// 向runloop里面添加事件source/timer/observer
[[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
}
我们将代码改成这样之后,发现没有调用 dealloc,也就是说我们达到了保活的效果了,当我们这个线程在执行完啊这个方法的时候就开始保活休眠睡觉了。
那我们测试下,这个线程是否一直保活。
- (void)testThread{
NSLog(@"test thread");
// 向runloop里面添加事件source/timer/observer
[[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
}
- (void)testAnother{
NSLog(@"test another");
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self performSelector:@selector(testAnother) onThread:_thread withObject:nil waitUntilDone:NO];
}
上面的代码意思就是,当我们点击屏幕的时候,就在这个子线程里面去执行任务,我们测试一下发现,每次都会打印
2020-02-19 16:22:25.217538+0800 blogTest[7806:425090] test thread
2020-02-19 16:22:26.498719+0800 blogTest[7806:425090] test another
2020-02-19 16:22:26.825388+0800 blogTest[7806:425090] test another
2020-02-19 16:22:27.056183+0800 blogTest[7806:425090] test another
2020-02-19 16:22:27.247697+0800 blogTest[7806:425090] test another
2020-02-19 16:22:27.439158+0800 blogTest[7806:425090] test another
2020-02-19 16:22:27.598440+0800 blogTest[7806:425090] test another
2020-02-19 16:22:27.757769+0800 blogTest[7806:425090] test another
2020-02-19 16:22:27.901013+0800 blogTest[7806:425090] test another
当我们将 保活的代码去掉试一下,发现就会打印一下,说明线程在执行完任务之后就死掉了。
所以当我们想在一个线程里面多次执行任务,还不想一直创建和销毁线程的时候,就可以选择线程保活的思想,将这个线程一直保活供我们使用。
销毁线程
上面的线程其实是一直在 run,虽然没有事情做,但是是一直存在的,没有销毁。我们需要改下代码
- (void)stop{
CFRunLoopStop(CFRunLoopGetCurrent());
}
- (void)stopRunloop{
[self performSelector:@selector(stop) onThread:_thread withObject:nil waitUntilDone:NO];
}
当我们需要停止到我们的runloop的时候,就手动调用 stopRunloop 方法就可以,这个方法会在当前这个保活线程去调用stop方法,我们的stop方法的意思是停止掉当前线程,这样就做到了我们的需求。
坑
其实上面是停止不了的,run 方法,内部会一直不断的调用 [[NSRunLoop currentRunLoop] runMode:<#(nonnull NSRunLoopMode)#> beforeDate:<#(nonnull NSDate *)#>];
这个方法啊,就好比,
while (1) {
[[NSRunLoop currentRunLoop] runMode:<#(nonnull NSRunLoopMode)#> beforeDate:<#(nonnull NSDate *)#>];
}
这样,我们只是停止了一次runmode,而 run方法内部其实会一直不断的执行,所以需要我们修改下代码
- (void)stop{
_stop = YES;
CFRunLoopStop(CFRunLoopGetCurrent());
}
- (void)stopRunloop{
[self performSelector:@selector(stop) onThread:_thread withObject:nil waitUntilDone:NO];
}
- (void)testThread{
NSLog(@"test thread");
// 向runloop里面添加事件source/timer/observer
[[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
while (!_stop) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
}
我们保活的代码,通过一个变量来控制,当我们需要停止的时候,就不会再继续run了,严谨点的话我们还需要在用到 ——thread 的前面都要加上判断,判断_thread还是否存在,在stop的时候要讲_thread给置为nil,然后while的时候要判断weakself是否存在。
网友评论