小编的语言表达能力不是很强,希望看到的大佬能理解我的意思,如果有什么问题欢迎各位大佬评论区留言。
这里有对NSThread进行一次封装,我传到了gitee上了,欢迎大家查看并指出问题。Demo传送门
使用场景
首先我们在UI线程中创建一个分线程 self.thread(这里的self.thread是继承于NSThread的子类,子类里面只有一句析构方法),线程开启成功之后,我们要通过RunLoop进行线程保活,在点击屏幕的时候要在self.thread这个线程中进行输出下面代码:
NSLog(@"在子线程中 触发了点击事件 当前线程为:%@",[NSThread currentThread]);
最后就是我们要查看一下这个self.thread的生命周期。
需要注意的点:
- 线程中创建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事件,即下面的总代码展示。
[[NSRunLoop currentRunLoop]run] 根据文档显示这么写的话,我们将没法进行停止RunLoop。
手动停止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