iOS底层系列之<29>--Runloop<五>Runloop中线程保活问题

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的时候,也能正常释放
    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值