什么是线程保活?
线程不死(线程处于激活状态,而不是NSThread实例对象被销毁),可以在同一个线程中做多件事,而且想什么时候做什么时候做。
我们以NSThread这个类来探讨线程保活的问题。看代码:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor yellowColor];
self.thread = [[LSThread alloc] initWithTarget:self selector:@selector(start) object:nil];
[self.thread start];
}
- (void)start{
NSLog(@"线程1开启做的第一件事...");
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self performSelector:@selector(doSth) onThread:self.thread withObject:nil waitUntilDone:NO];
}
- (void)doSth{
NSLog(@"在线程1中继续做其他事...");
}
其中的LSThread是继承自NSThread的。
当点击屏幕的时候,能在子线程self.thread中执行doSth方法吗?
通过代码,得到的答案是不能。这是为什么呢?因为子线程在执行完start方法之后就销毁了,所以不能再在该子线程中执行其他方法。为了让doSth方法执行,所以我们要让该子线程保活。
NSRunLoop保活线程
修改代码:
- (void)start{
NSLog(@"线程1开启做的第一件事...");
NSLog(@"线程1开启做的第一件事...");
NSLog(@"线程开始");
[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
NSLog(@"线程结束");
}
一个线程对应一个RunLoop,我们在子线程的RunLoop添加一个source1即NSPort对象,并跑在默认模式下,且时间是无限的,这样线程就能一直存在。
看打印:
2020-01-19 18:47:51.190543+0800 69.线程保活[8095:663010] 线程1开启做的第一件事...
2020-01-19 18:47:51.190933+0800 69.线程保活[8095:663010] 线程开始
2020-01-19 18:47:54.763623+0800 69.线程保活[8095:663010] 在线程1中继续做其他事...
线程保活的目的达到了,但这样的写法还有问题。
循环引用的问题
当我们退出当前控制器的时候,控制器LSThread对象没有被销毁,当前控制器也没有被销毁,这是因为两者发生了循环引用。
产生循环引用的代码:
self.thread = [[LSThread alloc] initWithTarget:self selector:@selector(start) object:nil];
我们可以联想以下NSTimer的类似创建方法,这样的创建实例的方法会让实例对target产生强引用。
解决循环引用
更改创建LSThread对象的方法,使用block
self.thread = [[LSThread alloc] initWithBlock:^{
NSLog(@"线程1开启做的第一件事...");
NSLog(@"线程1开启做的第一件事...");
NSLog(@"线程开始");
[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
NSLog(@"线程结束");
}];
我们把让线程保活的代码同样放在block中。
这样就解决了循环引用的问题,在退出当前控制器的时候,控制器也会被销毁,但此时我们使用的LSThread对象仍然不能被销毁,这个问题留到下篇文章解决。
网友评论