为何这段代码能使子线程持续存活?移除指定代码后线程却正常销毁
子线程无法自动销毁的原因分析
场景代码
- (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
相关产品推荐
相关产品推荐

