1、普通情况下,子线程声明周期
- (void)run {
NSLog(@"%s %@",__func__,[NSThread currentThread]);
[[NSRunLoop currentRunLoop] run];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSLog(@"%s",__func__);
JHThread *thread = [[JHThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[thread start];
}
打印结果:
为什么会这样?
1.CFRunLoopModeRef代表Runloop的运行模式
2.一个RunLoop包含若干个Mode,每个Mode又包含若干个Source0/Source1/Timer/Observer
3.Runloop启动时只能选择其中一个Mode,作为currentMode
4.如果需要切换Mode,只能退出当前Loop,再重新选择一个Mode进入
(a)不同组的Source0/Source1/Timer/Observer能分隔开来,互不影响
5.如果Mode里面没有任何Source0/Source1/Timer/Observer,Runloop会立马退出
第5点就阐述了原因了!
- 改进方法1
- (void)run {
NSLog(@"%s %@",__func__,[NSThread currentThread]);
// 添加任一 Source0/Source1/Timer/Observer,即可保证Runloop不会立马退出
[[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
}
- 改进方法2
- (void)viewDidLoad {
[super viewDidLoad];
self.thread = [[JHThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[self.thread start];
}
// 真正要做事的函数
- (void)test {
NSLog(@"%s",__func__);
}
// 这个方法的目的是为了线程保活
- (void)run {
NSLog(@"%s %@",__func__,[NSThread currentThread]);
// 添加任一 Source0/Source1/Timer/Observer,即可保证Runloop不会立马退出
[[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSLog(@"%s",__func__);
[self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:NO];
NSLog(@"123");
}
2、上面的能基本实现线程保活了,但是内存并没有释放
- 废话不多说了,直接上代码,可以释放内存
// [[NSRunLoop currentRunLoop] run]的伪代码
/**
BOOL shouldKeepRunning = YES; // global
NSRunLoop *theRL = [NSRunLoop currentRunLoop];
while (shouldKeepRunning && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
*/
所以说,这个方法会一直运行下去,停不下来!
- (void)viewDidLoad {
[super viewDidLoad];
self.isStopRL = NO;
__weak typeof(self) weakSelf = self;
self.thread = [[JHThread alloc] initWithBlock:^{
NSLog(@"---begin---%@",[NSThread currentThread]);
[[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
while (!weakSelf.isStopRL) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
NSLog(@"---end---%@",[NSThread currentThread]);
}];
[self.thread start];
}
- (IBAction)clickStop:(id)sender {
NSLog(@"%s",__func__);
self.isStopRL = YES;
[self performSelector:@selector(stopThread) onThread:self.thread withObject:nil waitUntilDone:NO];
}
- (void)stopThread {
CFRunLoopStop(CFRunLoopGetCurrent());
NSLog(@"%s %@",__func__,[NSThread currentThread]);
}
- (void)dealloc {
NSLog(@"%s",__func__);
}
// 真正要做事的函数
- (void)test {
NSLog(@"%s",__func__);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSLog(@"%s",__func__);
[self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:NO];
NSLog(@"123");
}
打印结果:
2022-01-21 16:13:23.062102+0800 super-interview[13865:169724] 拦截到按钮点击
2022-01-21 16:13:23.070544+0800 super-interview[13865:184744] —begin—<JHThread: 0x600001761b80>{number = 12, name = (null)}
2022-01-21 16:13:24.884739+0800 super-interview[13865:169724] -[ViewController touchesBegan:withEvent:]
2022-01-21 16:13:24.884877+0800 super-interview[13865:169724] 123
2022-01-21 16:13:24.884939+0800 super-interview[13865:184744] -[ViewController test]
2022-01-21 16:13:26.571435+0800 super-interview[13865:169724] -[ViewController clickStop:]
2022-01-21 16:13:26.571551+0800 super-interview[13865:169724] 拦截到按钮点击
2022-01-21 16:13:26.571611+0800 super-interview[13865:184744] -[ViewController stopThread] <JHThread: 0x600001761b80>{number = 12, name = (null)}
2022-01-21 16:13:26.571986+0800 super-interview[13865:184744] —end—<JHThread: 0x600001761b80>{number = 12, name = (null)}
2022-01-21 16:13:29.779486+0800 super-interview[13865:169724] -[ViewController dealloc]
2022-01-21 16:13:29.779642+0800 super-interview[13865:169724] -[JHThread dealloc]
3、终极完美方案
- (void)viewDidLoad {
[super viewDidLoad];
self.isStopRL = NO;
__weak typeof(self) weakSelf = self;
self.thread = [[JHThread alloc] initWithBlock:^{
NSLog(@"---begin---%@",[NSThread currentThread]);
[[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
while (weakSelf && !weakSelf.isStopRL) { // weakSelf 在退出的时候会为空
/**
BOOL shouldKeepRunning = YES; // global
NSRunLoop *theRL = [NSRunLoop currentRunLoop];
while (shouldKeepRunning && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
*/
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
NSLog(@"---end---%@",[NSThread currentThread]);
}];
[self.thread start];
}
- (IBAction)clickStop:(id)sender {
if (!self.thread) { // 如果已经销毁了,就不重复做事情了
return;
}
NSLog(@"%s",__func__);
[self performSelector:@selector(stopThread) onThread:self.thread withObject:nil waitUntilDone:YES]; // 这里要设置为同步,否则会引起坏内存访问
}
- (void)stopThread {
self.isStopRL = YES;
CFRunLoopStop(CFRunLoopGetCurrent());
NSLog(@"%s %@",__func__,[NSThread currentThread]);
self.thread = nil; // 防止重复销毁
}
- (void)dealloc {
NSLog(@"%s",__func__);
[self clickStop:nil]; // 点击back的时候,也能正常释放
}