美文网首页
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

    RunLoop应用:线程保活 线程保活、控制销毁 iOS-浅谈RunLoop8iOS底层原理总结 - RunLoo...

  • iOS 利用RunLoop 进行线程保活

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

  • iOS Runloop的理解与使用

    Runloop的概念 Runloop的存在主要就是为了线程保活,线程保活是为了线程能够及时的处理事件,不会在其执行...

  • iOS28 -- 利用runloop线程保活

    =========================================================...

  • iOS Runloop 线程保活及坑

    iOS Runloop 线程保活及坑 上面的代码, Thread 这个类继承自 NSThread ,然后重写了 d...

  • Runloop保活

    初始化一个NSThread对象以后,在线程回调方法里面进行操作,调起Runloop保活 如果runloop在运行,...

  • iOS笔记-runloop

    runloop可以做什么? 处理crash 保持线程存活(线程保活) 监测、优化卡顿 线程和runloop有什么关...

  • RunLoop 02 - 应用(线程保活)

    RunLoop 02 - 应用(线程保活) PermanentThread PermanentThread 使用示例

  • 线程保活

    线程保活是在多线程中进行耗时操作常用的功能: 常规开启方式,会出现内存泄漏 通过 [runloop run]直接...

  • RunLoop学习笔记

    参考深入理解RunLoop深入研究 Runloop 与线程保活RunLoop分享by孙源 RunLoop的概念 R...

网友评论

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

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