创建线程类
.h 文件下:
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
| #import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface ZJThread : NSThread
@end
NS_ASSUME_NONNULL_END
// // ZJThread.m // Runloop-线程保活 // // Created by Mac on 2023/2/16. //
#import "ZJThread.h"
@implementation ZJThread - (void)dealloc{ NSLog(@"%s",__func__); } @end
|
.m 文件下:
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
| #import "NextViewController.h"
@interface NextViewController () @end
@implementation NextViewController
- (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; }
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ // NSThread 频繁创建线程 ZJThread * thread = [[ZJThread alloc]initWithTarget:self selector:@selector(test) object:nil]; [thread start]; }
// 子线程需要执行的任务 - (void)test{ NSLog(@"%s %@", __func__, [NSThread currentThread]); }
- (void)dealloc{ NSLog(@"%s",__func__); }
@end
|
Run
起来之后
我们发现每次点击,都会去执行任务: ZJThread 频繁创建线程
1 2 3 4
| 2023-02-16 19:06:19.752652+0800 Runloop-频繁创建线程[16937:1026536] -[NextViewController test] <ZJThread: 0x28150ce80>{number = 6, name = (null)} 2023-02-16 19:06:19.753833+0800 Runloop-频繁创建线程[16937:1026536] -[ZJThread dealloc] 2023-02-16 19:06:20.309051+0800 Runloop-频繁创建线程[16937:1026537] -[NextViewController test] <ZJThread: 0x28152ca00>{number = 7, name = (null)} 2023-02-16 19:06:20.309697+0800 Runloop-频繁创建线程[16937:1026537] -[ZJThread dealloc]
|
可以看到任务一完成,线程就释放,并且这样 频繁的创建线程,很消耗资源
我们可以用 Runloop 来延长线程的生命周期,不让线程挂掉
运行一下:
1 2 3 4 5 6 7 8
| // 子线程需要执行的任务 - (void)test{ NSLog(@"%s %@", __func__, [NSThread currentThread]); [[NSRunLoop currentRunLoop] run]; NSLog(@"%s ----end----", __func__); }
|
结果:发现线程执行完成任务还是会结束线程 [ZJThread dealloc]
,这是因为 run
方法底层调用的是 - (BOOL)runMode:(NSRunLoopMode)mode beforeDate:(NSDate *)limitDate;
方法,这个方法会把 runloop
加入到 NSDefaultRunLoopMode
模式下,而 Model
下没有任何 Source0,Source1,Timer,Observer
,Runloop
会立马退出,所以我们需要往 Runloop
中添加任务
往 runloop 中添加任务,任何任务都行
1 2 3 4 5 6 7 8 9 10
| // 子线程需要执行的任务 - (void)test{ NSLog(@"%s %@", __func__, [NSThread currentThread]); NSLog(@"🏌🏌🏌🏌🏌🏌🏌🏀🏀🏀🏅🏅🏅"); // 往RunLoop里面添加 Source\Timer\Observer [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode]; [[NSRunLoop currentRunLoop] run]; NSLog(@"%s ----end----", __func__); }
|
Run:
1 2
| 2023-02-16 19:15:08.021488+0800 Runloop-频繁创建线程[16991:1030153] -[NextViewController test] <ZJThread: 0x281b95a40>{number = 22, name = (null)} 2023-02-16 19:15:08.021835+0800 Runloop-频繁创建线程[16991:1030153] 🏌🏌🏌🏌🏌🏌🏌🏀🏀🏀🏅🏅🏅
|
在运行线程就不会执行完任务就挂掉,而是执行完任务就休眠
每点击一次屏幕,都是创建了 ZJThread * thread = [[ZJThread alloc]initWithTarget:self selector:@selector(test) object:nil];
经过 [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
和 [[NSRunLoop currentRunLoop] run];
线程进入休眠,导致我们没办法唤醒线程和执行线程任务,继续修改
把 thread 改成局部变量
1
| self.thread = [[ZJThread alloc] initWithTarget:self selector:@selector(run) object:nil];
|
上面的写法:self
会引用 thread
,thread
会引用 self
,造成循环引用
用block实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| - (void)viewDidLoad { [super viewDidLoad]; // 如果使用如下方式创建thread,self会引用thread,thread会引用self,会造成循环引用。 // [[ZJThread alloc] initWithTarget:self selector:@selector(run) object:nil]; self.thread = [[ZJThread alloc] initWithBlock:^{ NSLog(@"%@----begin----", [NSThread currentThread]); // 往RunLoop里面添加 Source\Timer\Observer [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode]; // 线程会一直阻塞这这一行,永远不会销毁 [[NSRunLoop currentRunLoop] run];
// 当把NSRunLoop停掉之后,代码就会从下一行往下走,这时候任务执行完成,线程该死的时候就会死了。 NSLog(@"%@----end----", [NSThread currentThread]); }]; [self.thread start]; }
|
run
方法:Runloop
无法停止,它专门用于开启一个永不销毁的线程
替换 run
1
| [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
|
Runloop 的启动和退出
启动:
- run:无条件
- runUntilDate:设定时间限制
- runMode:beforeDate:在特定模式下
停止:
- CFRunLoopStop(CFRunLoopGetCurrent())
实现线程保活
ZJThread.h 中:
1 2 3 4 5 6 7
| #import "ZJThread.h"
@implementation ZJThread - (void)dealloc{ NSLog(@"%s",__func__); } @end
|
NextVC 中:
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 88 89 90 91 92 93 94 95
| #import "NextViewController.h" #import "ZJThread.h"
@interface NextViewController () @property (nonatomic,strong)ZJThread * thread; @property (nonatomic,assign)BOOL isStop; @property (nonatomic,strong)UIButton * stopBtn;
@end
@implementation NextViewController
- (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; // 停止按钮 [self.view addSubview:self.stopBtn]; /* 创建线程有两种方式: target:会造成循环引用 block:不会造成循环引用 */ [self blockCreatThread]; } - (void)blockCreatThread{ __weak typeof(self) weakSelf = self; self.isStop = NO; // 未停止 self.thread = [[ZJThread alloc] initWithBlock:^{ NSLog(@"%@---begin---",[NSThread currentThread]); // 往runloop中添加Source\Timer\Observer [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode]; while (weakSelf && !weakSelf.isStop) { [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; } NSLog(@"%@---end---", [NSThread currentThread]); /* run方法无法停止,它专门用于开启一个永不销毁的线程 [[NSRunLoop currentRunLoop] run]; */ }]; [self.thread start]; }
// 点击 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:NO]; } // 子线程要执行的任务 - (void)test{ NSLog(@"%s %@", __func__, [NSThread currentThread]); NSLog(@"🏌🏌🏌🏌🏌🏌🏌🏀🏀🏀🏅🏅🏅"); }
// 停止 - (void)stop{ if (!self.thread) return; // 停止子线程 [self performSelector:@selector(stopThread) onThread:self.thread withObject:nil waitUntilDone:YES]; } - (void)stopThread{ // 标记停止 self.isStop = YES; // 停止 CFRunLoopStop(CFRunLoopGetCurrent()); self.thread = nil; NSLog(@"%@---end---", [NSThread currentThread]); }
- (void)dealloc{ NSLog(@"%s",__func__); // 停止子线程 [self stop]; }
// - (UIButton *)stopBtn{ if (!_stopBtn) { _stopBtn = [[UIButton alloc] initWithFrame:CGRectMake(100, 100, 100, 100)]; _stopBtn.backgroundColor = [UIColor greenColor]; [_stopBtn setTitle:@"stop" forState:UIControlStateNormal]; [_stopBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; [_stopBtn addTarget:self action:@selector(stop) forControlEvents:UIControlEventTouchUpInside]; } return _stopBtn; }
@end
|