iOS 利用RunLoop 进行线程保活

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

这里有对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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容

  • 老师开示: 世上最难打开的是心门,最难走的路是心路。最难调的是心态,最难过的桥是心桥。 希望我们要乐观...
    蒲圆阅读 195评论 0 0
  • 我见过的你阅读 132评论 0 0
  • 在威廉古堡上班幸福吗? 自述一段在威廉古堡上班的感受吧,毕竟这也是我第一次上班。 从进入威廉古堡开始我已经待了有一...
    de815cddfddd阅读 166评论 0 0
  • 自我感觉不错,把该看的课程全部看完了。 虽然没有想到可能之后还有个测试,不过相信自己认真做了一个笔记本的笔记,应该...
    我对这个世界知之甚少阅读 48评论 0 0
  • 久违的晴天,家长会。 家长大会开好到教室时,离放学已经没多少时间了。班主任说已经安排了三个家长分享经验。 放学铃声...
    飘雪儿5阅读 7,476评论 16 22