先上代码,我在上面都加了注释:
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface GYJThread : NSObject
///在子线程执行任务
- (void)excuteAction:(void(^)(void))task;
///销毁runloop和子线程
- (void)stop;
@end
#import "GYJThread.h"
@interface textThread : NSThread
@end
@implementation textThread
- (void)dealloc
{
NSLog(@"%s",__func__);
}
@end
@interface GYJThread ()
@property(nonatomic,strong)textThread *thread;
@end
@implementation GYJThread
- (instancetype)init
{
if (self = [super init]) {
//开启一条线程
self.thread = [[textThread alloc]initWithBlock:^{
NSLog(@"runloop开启");
//runloop与线程一一对应,通过获取方式,底层就会去创建,类似于懒加载
CFRunLoopRef runloop = CFRunLoopGetCurrent();
//添加timer source block的方式,用以让runloop处理事情,CFRunLoopSourceContext结构体,初始化结构体,类似int a = 0;
CFRunLoopSourceContext context = {0};
CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
//添加的source已默认模式处理(默认模式/滚动模式)
CFRunLoopAddSource(runloop, source, kCFRunLoopDefaultMode);
//释放创建的source (source0 source1 响应点击等操作的内容就放在source中,timer定时器)
CFRelease(source);
//让runloop工作,1.0e10代表一个很大的时间(让runloop一直工作),false代表runloop处理该消息后不退出,如果传true,马上就会打印runloop结束,传false是需要runloop一直卡在这
//CFRunLoopRun() 该api会导致runloop一直存在,不会销毁,我们控制不了他销毁,所以用CFRunLoopRunInMode
CFRunLoopRunInMode(kCFRunLoopDefaultMode,1.0e10, false);
NSLog(@"runloop结束");
}];
//开启线程
[self.thread start];
}
return self;
}
///在子线程执行任务
- (void)excuteAction:(void(^)(void))task
{
if (!self.thread) return;
//在子线程执行任务waitUntilDone:传NO 代表主线程和当前任务互不干扰,传YES代表必须执行完这个任务 主线程才会往下走
[self performSelector:@selector(priviteExcuteAction:) onThread:self.thread withObject:task waitUntilDone:NO];
}
///销毁runloop和子线程
- (void)stop
{
if (!self.thread) return;
//在子线程执行任务waitUntilDone:传NO 代表主线程和当前任务互不干扰,传YES代表必须执行完这个任务 主线程才会往下走
[self performSelector:@selector(priviteStop) onThread:self.thread withObject:nil waitUntilDone:YES];
}
- (void)priviteExcuteAction:(void(^)(void))task
{
NSLog(@"当前线程%@",[NSThread currentThread]);
task();
}
- (void)priviteStop
{
//关闭runloop
CFRunLoopStop(CFRunLoopGetCurrent());
self.thread = nil;
}
- (void)dealloc
{
[self stop];
}
@end
然后新建2个控制器,用以来验证runloop和控制器,线程的生命周期
![](https://img.haomeiwen.com/i3175826/076fb6ccbe86b9f6.png)
![](https://img.haomeiwen.com/i3175826/b928831b8a4f04b3.png)
代码比较简单,ViewController1中间加了个按钮,点击push到ViewController2中
ViewController2中间加了个停止线程按钮,点击屏幕会执行任务
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor yellowColor];
self.stopBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
self.stopBtn.center = self.view.center;
[self.view addSubview:self.stopBtn];
[self.stopBtn setTitle:@"停止线程" forState:UIControlStateNormal];
[self.stopBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[self.stopBtn addTarget:self action:@selector(stop) forControlEvents:UIControlEventTouchUpInside];
self.customThread = [[GYJThread alloc]init];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self.customThread excuteAction:^{
NSLog(@"执行任务");
}];
}
- (void)stop
{
[self.customThread stop];
}
- (void)dealloc
{
NSLog(@"%s",__func__);
}
A.进入ViewController2,点击空白屏幕,再点击停止按钮
![](https://img.haomeiwen.com/i3175826/13f0b01f90ccf4d2.png)
可以看到任务都是在同一条子线程执行,点击停止,runloop和线程都销毁了,此时再点击空白屏幕,不会有反应了,为了防止多次点击按钮导致奔溃,所以在执行任务和stop都做一个判断,防止点击一次按钮后,东西都销毁了,再次点击,导致奔溃
B.进入ViewController2,点击空白屏幕,直接点击返回按钮
![](https://img.haomeiwen.com/i3175826/31a049243cece266.png)
可以看到子线程,runloop,都会跟随ViewController2的销毁一起销毁
这个类可以包装成工具,其中的打印信息和textThread(都是为了调试用),后面是可以删除打印信息并将textThread替换成NSThread的,好了,分享完毕
网友评论