iOS 利用RunLoop 进行线程保活

2021-01-07  本文已影响0人  陶小亮

小编的语言表达能力不是很强,希望看到的大佬能理解我的意思,如果有什么问题欢迎各位大佬评论区留言。

这里有对NSThread进行一次封装,我传到了gitee上了,欢迎大家查看并指出问题。Demo传送门

使用场景
首先我们在UI线程中创建一个分线程 self.thread(这里的self.thread是继承于NSThread的子类,子类里面只有一句析构方法),线程开启成功之后,我们要通过RunLoop进行线程保活,在点击屏幕的时候要在self.thread这个线程中进行输出下面代码:

NSLog(@"在子线程中 触发了点击事件 当前线程为:%@",[NSThread currentThread]);

最后就是我们要查看一下这个self.thread的生命周期。

需要注意的点:

  1. 线程中创建RunLoop是通过获取RunLoop的方式的;如果
self.thread = [[ZLThread alloc]initWithBlock:^{
        NSLog(@"分线程开启成功,当前线程 %@",[NSThread currentThread]);
        
        ///开启RunLoop
        ###这里的代码###

        while (weakSelf && !weakSelf.isStoped) {
            ///当条件满足的时候,则执行这里的代理
            ///[NSDate distantFuture] 未来
            NSLog(@"weakSelf___%@",weakSelf);
            [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
        }
        
    //    ///这个方法会导致RunLoop一直在循环做事儿,没办法停止的
    //    [[NSRunLoop currentRunLoop] run];
        NSLog(@"-----end-----");
    }];
    [self.thread start];

如果 ###这里的代码### 替换成:[NSRunLoop currentRunLoop];的话,这里的RunLoop创建之后并没有执行任何的任务:source0,source1,timer,Observe。起不到保活的效果,这里需要给RunLoop添加一个source1事件,即下面的总代码展示。

  1. [[NSRunLoop currentRunLoop]run] 根据文档显示这么写的话,我们将没法进行停止RunLoop。

  2. 手动停止RunLoop只能通过C语言的方式来:CFRunLoopStop(CFRunLoopGetCurrent());这个停止方法只能将[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
    停止一次,一旦RunLoop运行之后,这里会一直进行睡眠等待,所以我们需要一个标识来记录是否需要进入睡眠,即self.isStoped,是否停止RunLoop。

[self performSelector:@selector(stopRunLoop) onThread:self.thread withObject:nil waitUntilDone:YES];

waitUntilDone 这里的参数含义是:是否等子线程中的任务执行完毕之后再执行后面的代码(这里是指的UI线程中的代码)这里要设置成YES,不然会造成野指针的问题,

5.在controller的析构方法中一定要执行手动停止RunLoop,一旦调用之后,会出现

 self.thread = [[ZLThread alloc]initWithBlock:^{
        NSLog(@"分线程开启成功,当前线程 %@",[NSThread currentThread]);
        
        ///开启RunLoop
        [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc]init] forMode:NSDefaultRunLoopMode];

#warning 这里的weakSelf会为空。因为析构方法已经走了,self会置为nil。

        while (weakSelf && !weakSelf.isStoped) {
            ///当条件满足的时候,则执行这里的代理
            ///[NSDate distantFuture] 未来
            NSLog(@"weakSelf___%@",weakSelf);
            [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
        }
        
    //    ///这个方法会导致RunLoop一直在循环做事儿,没办法停止的
    //    [[NSRunLoop currentRunLoop] run];
        NSLog(@"-----end-----");
    }];

注意 以上代码是下面的代码进行了修改的,下面的代码是完整的代码

总代码展示:

//
//  RunLoopController.m
//  TZLDemo
//
//  Created by Yuki on 2021/1/7.
//  Copyright © 2021 Tzl. All rights reserved.
//
/**
 创建一个线程,
 通过RunLoop让线程保活,
 然后在线程中执行其他的任务
 **/
#import "RunLoopController.h"
#import "ZLThread.h"///自定的线程

@interface RunLoopController ()
/**  线程  **/
@property (nonatomic , strong)ZLThread  *thread;
/**  是否停止RunLoop  **/
@property (nonatomic , assign)BOOL  isStoped;
@end

@implementation RunLoopController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = UIColor.whiteColor;
    
    ///默认runLoop没有停止
    self.isStoped = NO;
    
    ///创建一个分线程
    ///这种方法是持有了self,会造成循环引用
//    self.thread = [[ZLThread alloc] initWithTarget:self selector:@selector(run) object:nil];
    
    __weak typeof(self)weakSelf = self;
    self.thread = [[ZLThread alloc]initWithBlock:^{
        NSLog(@"分线程开启成功,当前线程 %@",[NSThread currentThread]);
        
        ///开启RunLoop
        [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc]init] forMode:NSDefaultRunLoopMode];
        while (weakSelf && !weakSelf.isStoped) {
            ///当条件满足的时候,则执行这里的代理
            ///[NSDate distantFuture] 未来
            NSLog(@"weakSelf___%@",weakSelf);
            [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
        }
        
    //    ///这个方法会导致RunLoop一直在循环做事儿,没办法停止的
    //    [[NSRunLoop currentRunLoop] run];
        NSLog(@"-----end-----");
    }];
    [self.thread start];
    
    ///手动停止runLoop
    UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
    btn.frame = CGRectMake(100, 100, 200, 40);
    [btn setTitle:@"手动停止runLoop" forState:UIControlStateNormal];
    [btn setTitleColor:UIColor.redColor forState:UIControlStateNormal];
    btn.titleLabel.font = [UIFont systemFontOfSize:16];
    [btn addTarget:self action:@selector(handleEndRunLoop) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:btn];
}
///点击屏幕
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    if (!self.thread) return;///如果线程不存在,直接返回
    [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:YES];
}
///点击屏幕执行的事件
- (void)test{
    NSLog(@"在子线程中 触发了点击事件 当前线程为:%@",[NSThread currentThread]);
}

///手动停止runLoop
- (void)handleEndRunLoop{
    ///停止runLoop
    ///如果线程不存在,直接返回
    if (!self.thread) return;
    //    waitUntilDone 是否等子线程中的任务执行完毕之后再执行后面的代码
    [self performSelector:@selector(stopRunLoop) onThread:self.thread withObject:nil waitUntilDone:YES];
}
///停止RunLoop
- (void)stopRunLoop{
    ///获取到当前的线程中的runLoop
    self.isStoped = YES;
    CFRunLoopStop(CFRunLoopGetCurrent());
    ///清空子线程
    self.thread = nil;
}


/////分线程开启
//- (void)run{
//    NSLog(@"分线程开启成功,当前线程 %@",[NSThread currentThread]);
//    [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc]init] forMode:NSDefaultRunLoopMode];
//    while (!self.isStoped) {
//        ///当条件满足的时候,则执行这里的代理
//        ///[NSDate distantFuture] 未来
//        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
//    }
//
////    ///这个方法会导致RunLoop一直在循环做事儿,没办法停止的
////    [[NSRunLoop currentRunLoop] run];
//    NSLog(@"end-----");
//}

///释放控制器
- (void)dealloc{
    NSLog(@"%s",__func__);
    ///怎么释放??
    [self handleEndRunLoop];
}

@end
上一篇下一篇

猜你喜欢

热点阅读