1、基本思路
1.1、创建一个Helper类对NSThread类进行封装;
1.2、Helper类内部实现线程的创建、执行、销毁等一系列操作;
1.3、核心点在于RunLoop的循环执行,调用runMode:beforeDate方法让线程处于休眠状态,再通过while循环,使runloop执行结束后再次开启,循环条件外部控制,这样就能达到控制runloop的生命周期,从而保全线程的生命周期;
1.4、Helper类在销毁时,要及时更改runloop的循环条件,并唤醒或停止子线程中的runloop,runloop停止后线程结束,否则两者会一直存在内存当中,runloop因为没有及时被唤醒而一直处于休眠状态,线程因为runloop没有停止而无法释放;
1.5、Helper在使用NSThread创建子线程时也要注意循环引用的问题;
2、代码示例
2.1、内部实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | // // SAThreadHelper.h // RunLoop // // Created by 余西安 on 2018/11/24. // Copyright © 2018 yusian. All rights reserved. // #import "SAThreadHelper.h" @interface _SAInnerThread : NSThread @end @implementation _SAInnerThread - (instancetype)init { if (self = [super init]){ self.name = @"com.yusian.inner-thread"; } return self; } - (void)dealloc { NSLog(@"%s", __func__); } @end @interface SAThreadHelper () @property (nonatomic, assign, getter=isStopped) BOOL stopped; @property (nonatomic, strong) _SAInnerThread *innerThread; @end @implementation SAThreadHelper - (instancetype)init { if (self = [super init]){ self.stopped = NO; __weak typeof(self) weakself = self; self.innerThread = [[_SAInnerThread alloc] initWithBlock:^{ // 1、获取当前线程的RunLoop NSRunLoop *runloop = [NSRunLoop currentRunLoop]; // 2、给RunLoop添加一个Source,以免自动销毁 [runloop addPort:NSPort.new forMode:NSDefaultRunLoopMode]; // 3、RunLoop循环,任务执行结束后如没有再次开启则结束 while (weakself && !weakself.isStopped) { // 3.1、开启RunLoop,当没有任务时自动进入休眠状态; // 3.2、进入休眠状态表明当前任务没有结束,线程就不会销毁 [runloop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; } NSLog(@"--------RunLoop was stopped..."); }]; [self.innerThread start]; NSLog(@"--------RunLoop is Starting..."); } return self; } #pragma mark - 外部调用 - (void)executeWithBlock:(void (^)(void))block { // 外部调用时,如果当前线程已销毁或没有执行内容则返回; if (!self.innerThread || !block) return; // 在创建的子线程中执行外部任务,waitUntilDone可设置为NO,异步执行; [self performSelector:@selector(__threadExecuteWithBlock:) onThread:self.innerThread withObject:block waitUntilDone:NO]; } - (void)stop { if (!self.innerThread) return; // 结束当前线程工作,只要停止当前线程中的RunLoop,线程中RunLoop没有任务可执行线程即将结束; // 注意这里waitUntilDone要设置为YES,同步进行,因当子线程会和当前控制器的主线程有线程间通讯,如果异步进行会因为主线程提前被销毁而出现野指针异常 [self performSelector:@selector(__threadStop) onThread:self.innerThread withObject:nil waitUntilDone:YES]; self.innerThread = nil; } #pragma mark - 内部实现 - (void)__threadExecuteWithBlock:(void (^)(void))block { if (block) block(); } - (void)__threadStop { // 在当前线程执行一个任务,并且执行的是停止当前RunLoop动作 // 不执行停止动作,RunLoop也会因为执行了动作而被唤醒,结束 CFRunLoopStop(CFRunLoopGetCurrent()); } - (void)dealloc { NSLog(@"%s", __func__); self.stopped = YES; [self stop]; } @end |
2.2、外部调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #import "ViewController.h" #import "SAThreadHelper.h" @interface ViewController () @property (nonatomic, strong) SAThreadHelper *threadHelper; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.threadHelper = [SAThreadHelper new]; } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { [self.threadHelper executeWithBlock:^{ NSLog(@"%@", [NSThread currentThread]); }]; } - (void)dealloc { NSLog(@"%s", __func__); } @end |
2.3、运行结果:
2018-11-24 12:06:14.303814+0800 RunLoop[2704:138157] ——–RunLoop is Starting…
2018-11-24 12:06:16.289095+0800 RunLoop[2704:138157] -[ViewController dealloc]
2018-11-24 12:06:16.289543+0800 RunLoop[2704:138157] -[SAThreadHelper dealloc]
2018-11-24 12:06:16.290330+0800 RunLoop[2704:144194] ——–RunLoop was stopped…
2018-11-24 12:06:16.291644+0800 RunLoop[2704:144194] -[_SAInnerThread dealloc]
线程创建过程中开启RunLoop这个关键过程也可以通过CoreFoundtion中的CFRunLoopRef相关函数来实现,其实NSRunLoop就是NSFoundation中利用Objective-C对CFRunLoopRef相关方法的封装,所以直接使用CFRunLoopRef相关函数更加灵活
运行结果:
2018-11-24 13:29:35.737409+0800 RunLoop[3713:273009] ——–RunLoop is Starting…
2018-11-24 13:29:36.664308+0800 RunLoop[3713:273326] <_sainnerthread: 0x600000158bc0>{number = 3, name = com.yusian.inner-thread}
2018-11-24 13:29:37.361354+0800 RunLoop[3713:273326] <_sainnerthread: 0x600000158bc0>{number = 3, name = com.yusian.inner-thread}
2018-11-24 13:29:39.810810+0800 RunLoop[3713:273009] -[ViewController dealloc]
2018-11-24 13:29:39.811084+0800 RunLoop[3713:273009] -[SAThreadHelper dealloc]
2018-11-24 13:29:39.811946+0800 RunLoop[3713:273326] ——–RunLoop was stopped…
2018-11-24 13:29:39.813122+0800 RunLoop[3713:273326] -[_SAInnerThread dealloc]