iOS 线程保活如何实现?
标签:
编程学习
iOS学习
iOS开发中经常会用到多线程的技术,每次使用都需要创建线程,这样会消耗很多的资源,能不能让线程常驻呢?
线程使用
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.thread = [[DCThread alloc] initWithBlock:^{
NSLog(@"---%@-----执行完毕", [NSThread currentThread]);
}];
[self.thread start];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSLog(@"点击了");
[self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:NO];
}
- (void)test {
NSLog(@"%s %@", __func__, [NSThread currentThread]);
}
看代码,我们创建了一个线程,block中的代码执行完成之后点击界面发现该线程已经不能用了,那是因为该线程在执行完之后就退出了,如何处理这个问题呢?答案就是使用 Runloop 技术使线程保活,不使用时睡眠,使用时激活就行了
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.thread = [[DCThread alloc] initWithBlock:^{
[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
NSLog(@"---%@-----执行完毕", [NSThread currentThread]);
}];
[self.thread start];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSLog(@"点击了");
[self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:NO];
}
- (void)test {
NSLog(@"%s %@", __func__, [NSThread currentThread]);
}
添加上 Runloop 的代码之后就发现会卡在 run 那里,只要点击界面就会执行 test 方法。 但是这里有一个问题,就是 run 这个方法的官方解释
If you want the run loop to terminate, you shouldn't use this method
你还想从 runloop 里面退出来,就不能用 run 方法。
这样就无法释放了,所以我们做一下修改,使用有模式的方式去运行
[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
/// 不这样写也只是执行一次
while (1) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
使用 while 循环是为了保证能够一直停留在这里,但是这样又不可控,所以进行改造一下,比较完美
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.isStoped = false;
__weak typeof(self) weakSelf = self;
self.thread = [[DCThread alloc] initWithBlock:^{
// 往RunLoop里面添加Source\Timer\Observer
[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
while (weakSelf && !weakSelf.isStoped) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
NSLog(@"---%@-----执行完毕", [NSThread currentThread]);
}];
[self.thread start];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSLog(@"点击了");
[self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:NO];
}
- (void)test {
NSLog(@"%s %@", __func__, [NSThread currentThread]);
}
- (IBAction)stop:(id)sender {
if (!self.thread) return;
// 在子线程调用stop(waitUntilDone设置为YES,代表子线程的代码执行完毕后,这个方法才会往下走)
[self performSelector:@selector(stopThread) onThread:self.thread withObject:nil waitUntilDone:YES];
}
- (void)stopThread {
// 设置标记为YES
self.isStoped = YES;
// 停止RunLoop
CFRunLoopStop(CFRunLoopGetCurrent());
NSLog(@"%s %@", __func__, [NSThread currentThread]);
// 清空线程
self.thread = nil;
}
- (void)dealloc {
NSLog(@"controller 死了");
[self stopThread];
}
工具封装
既然是为了保活线程重复利用,那直接写在 ViewController 中肯定不行,我们这里封装一下
@interface DCThread()
///不允许外部调用
@property (strong, nonatomic) NSThread *innerThread;
///控制是否停止
@property (assign, nonatomic, getter=isStopped) BOOL stopped;
@end
@implementation DCThread
- (instancetype)init {
if (self = [super init]) {
self.stopped = NO;
__weak typeof(self) weakSelf = self;
self.innerThread = [[NSThread alloc] initWithBlock:^{
[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
while (weakSelf && !weakSelf.isStopped) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
}];
/// 自动开启
[self.innerThread start];
}
return self;
}
/// 外部暴露的接口,执行一个任务
- (void)executeTask:(DCThreadBlock)task {
if (!self.innerThread || !task) return;
[self performSelector:@selector(__executeTask:) onThread:self.innerThread withObject:task waitUntilDone:NO];
}
- (void)dealloc {
/// 自动释放
NSLog(@"%s", __func__);
[self stopTask];
}
///自动调用停止
- (void)stopTask {
if (!self.innerThread) return;
[self performSelector:@selector(__stop) onThread:self.innerThread withObject:nil waitUntilDone:YES];
}
- (void)__stop {
self.stopped = YES;
CFRunLoopStop(CFRunLoopGetCurrent());
self.innerThread = nil;
}
- (void)__executeTask:(DCThreadBlock)task {
task();
}
@end