先上代碼,我在上面都加了注釋:
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface GYJThread : NSObject
///在子線(xiàn)程執(zhí)行任務(wù)
- (void)excuteAction:(void(^)(void))task;
///銷(xiāo)毀runloop和子線(xiàn)程
- (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]) {
//開(kāi)啟一條線(xiàn)程
self.thread = [[textThread alloc]initWithBlock:^{
NSLog(@"runloop開(kāi)啟");
//runloop與線(xiàn)程一一對(duì)應(yīng),通過(guò)獲取方式,底層就會(huì)去創(chuàng)建,類(lèi)似于懶加載
CFRunLoopRef runloop = CFRunLoopGetCurrent();
//添加timer source block的方式,用以讓runloop處理事情,CFRunLoopSourceContext結(jié)構(gòu)體,初始化結(jié)構(gòu)體,類(lèi)似int a = 0;
CFRunLoopSourceContext context = {0};
CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
//添加的source已默認(rèn)模式處理(默認(rèn)模式/滾動(dòng)模式)
CFRunLoopAddSource(runloop, source, kCFRunLoopDefaultMode);
//釋放創(chuàng)建的source (source0 source1 響應(yīng)點(diǎn)擊等操作的內(nèi)容就放在source中,timer定時(shí)器)
CFRelease(source);
//讓runloop工作,1.0e10代表一個(gè)很大的時(shí)間(讓runloop一直工作),false代表runloop處理該消息后不退出,如果傳true,馬上就會(huì)打印runloop結(jié)束,傳false是需要runloop一直卡在這
//CFRunLoopRun() 該api會(huì)導(dǎo)致runloop一直存在,不會(huì)銷(xiāo)毀,我們控制不了他銷(xiāo)毀,所以用CFRunLoopRunInMode
CFRunLoopRunInMode(kCFRunLoopDefaultMode,1.0e10, false);
NSLog(@"runloop結(jié)束");
}];
//開(kāi)啟線(xiàn)程
[self.thread start];
}
return self;
}
///在子線(xiàn)程執(zhí)行任務(wù)
- (void)excuteAction:(void(^)(void))task
{
if (!self.thread) return;
//在子線(xiàn)程執(zhí)行任務(wù)waitUntilDone:傳NO 代表主線(xiàn)程和當(dāng)前任務(wù)互不干擾,傳YES代表必須執(zhí)行完這個(gè)任務(wù) 主線(xiàn)程才會(huì)往下走
[self performSelector:@selector(priviteExcuteAction:) onThread:self.thread withObject:task waitUntilDone:NO];
}
///銷(xiāo)毀runloop和子線(xiàn)程
- (void)stop
{
if (!self.thread) return;
//在子線(xiàn)程執(zhí)行任務(wù)waitUntilDone:傳NO 代表主線(xiàn)程和當(dāng)前任務(wù)互不干擾,傳YES代表必須執(zhí)行完這個(gè)任務(wù) 主線(xiàn)程才會(huì)往下走
[self performSelector:@selector(priviteStop) onThread:self.thread withObject:nil waitUntilDone:YES];
}
- (void)priviteExcuteAction:(void(^)(void))task
{
NSLog(@"當(dāng)前線(xiàn)程%@",[NSThread currentThread]);
task();
}
- (void)priviteStop
{
//關(guān)閉runloop
CFRunLoopStop(CFRunLoopGetCurrent());
self.thread = nil;
}
- (void)dealloc
{
[self stop];
}
@end
然后新建2個(gè)控制器,用以來(lái)驗(yàn)證runloop和控制器,線(xiàn)程的生命周期
代碼比較簡(jiǎn)單,ViewController1中間加了個(gè)按鈕,點(diǎn)擊push到ViewController2中
ViewController2中間加了個(gè)停止線(xiàn)程按鈕,點(diǎn)擊屏幕會(huì)執(zhí)行任務(wù)
- (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:@"停止線(xiàn)程" 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(@"執(zhí)行任務(wù)");
}];
}
- (void)stop
{
[self.customThread stop];
}
- (void)dealloc
{
NSLog(@"%s",__func__);
}
A.進(jìn)入ViewController2,點(diǎn)擊空白屏幕,再點(diǎn)擊停止按鈕
可以看到任務(wù)都是在同一條子線(xiàn)程執(zhí)行,點(diǎn)擊停止,runloop和線(xiàn)程都銷(xiāo)毀了,此時(shí)再點(diǎn)擊空白屏幕,不會(huì)有反應(yīng)了,為了防止多次點(diǎn)擊按鈕導(dǎo)致奔潰,所以在執(zhí)行任務(wù)和stop都做一個(gè)判斷,防止點(diǎn)擊一次按鈕后,東西都銷(xiāo)毀了,再次點(diǎn)擊,導(dǎo)致奔潰
B.進(jìn)入ViewController2,點(diǎn)擊空白屏幕,直接點(diǎn)擊返回按鈕
可以看到子線(xiàn)程,runloop,都會(huì)跟隨ViewController2的銷(xiāo)毀一起銷(xiāo)毀
這個(gè)類(lèi)可以包裝成工具,其中的打印信息和textThread(都是為了調(diào)試用),后面是可以刪除打印信息并將textThread替換成NSThread的,好了,分享完畢