美文网首页
iOS 利用RunLoop 进行线程保活

iOS 利用RunLoop 进行线程保活

作者: 陶小亮 | 来源:发表于2021-01-07 17:27 被阅读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
    

    相关文章

      网友评论

          本文标题:iOS 利用RunLoop 进行线程保活

          本文链接:https://www.haomeiwen.com/subject/ypozoktx.html