You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何这段代码能使子线程持续存活?移除指定代码后线程却正常销毁

子线程无法自动销毁的原因分析

场景代码

- (void)testCreateContinuousChildThread {
    testThread *thread = [[testThread alloc] initWithBlock:^{
        NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 repeats:NO block:^(NSTimer * _Nonnull timer) {
            NSLog(@"current thread:%@", [NSThread currentThread]);
        }];
        NSRunLoop *loop = [NSRunLoop currentRunLoop];
        [loop addTimer:timer forMode:NSRunLoopCommonModes];
        [loop run];
    }];
    
    [thread start];
    
    [self performSelector:@selector(testSelector) onThread:thread withObject:nil waitUntilDone:YES];
}

- (void)testSelector {
    NSLog(@"testSelector");
}

testThread类仅包含用于打印销毁日志的dealloc方法。运行上述代码后,testThread无法自动销毁;但移除[self performSelector:@selector(testSelector) onThread:thread withObject:nil waitUntilDone:YES];这行代码后,testThread会正常销毁。

原因拆解

1. 无performSelector时的线程销毁逻辑

当没有调用跨线程performSelector时:

  • 子线程启动后,RunLoop添加了一个一次性NSTimer,随后调用[loop run]启动RunLoop。
  • 一次性Timer触发执行完成后,会从RunLoop中被移除,此时RunLoop没有任何剩余的事件源(Timer、端口、自定义源都不存在)。
  • RunLoop的run方法检测到没有事件源后,会自动退出,子线程的block执行完毕,线程生命周期结束。
  • testThread实例此时没有额外的强引用,会被系统释放,触发dealloc打印销毁日志。

2. 调用performSelector后线程存活的原因

调用performSelector:onThread:waitUntilDone:YES后:

  • 这个方法的底层会往目标子线程的RunLoop中添加一个临时端口(port),用来传递方法调用的消息。
  • 虽然testSelector已经执行完成,但这个临时端口并没有被自动清理,RunLoop会认为还有潜在的事件需要处理,因此不会退出。
  • RunLoop持续运行导致子线程的block无法执行完毕,线程一直处于存活状态,testThread实例被线程管理机制强持有,无法释放,也就不会触发dealloc。

关键原理:RunLoop的存活条件

RunLoop要持续运行,必须至少持有一个有效的事件源(Timer、Port、自定义源),或者被设置为循环调用的运行模式。当所有事件源都被移除后,默认的run方法会自动退出,线程才会结束生命周期。

内容的提问来源于stack exchange,提问作者Grayson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 04:19:53