RunLoop 与线程的关系
苹果官方文档中,RunLoop
的相关介绍写在线程编程指南中,可见RunLoop
和线程的关系不一般。Threading Programming Guide(苹果官方文档)
RunLoop对象和线程是一一对应关系;
RunLoop保存在一个全局的Dictionary里,线程作为Key,RunLoop作为value;
如果没有RunLoop,线程执行完全任务就会退出;如果没有RuLoop,主线程执行完main()函数就会退出,退出程序就不能处于运行状态;
RunLoop创建时机:线程刚创建时并没有RunLoop对象,RunLoop会在第一次获取它时创建;
RunLoop销毁时机:RunLoop会在线程结束时销毁;
主线程的RunLoop已经自动获取(创建),子线程默认没有开启RunLoop;
主线程的RunLoop对象是在UIApplicationMain中通过[NSRunLoop currentRunLoop]获取,一旦发现它不存在,就会创建RunLoop对象。
未启动 RunLoop 的子线程
创建一个NSThread的子类HTThread并重写了dealloc方法来观察线程的状态。执行以下代码,发现子线程执行完一次test任务就退出销毁了,没有再次执行test任务,原因就是没有启动改线程的RunLoop。
- (void)viewDidLoad {
[super viewDidLoad];
HTThread *thread = [[HTThread alloc] initWithTarget:self selector:@selector(test) object:nil];
[thread start];
[self performSelector:@selector(test) onThread:thread withObject:nil waitUntilDone:NO];
}
- (void)test {
NSLog(@"test on %@", [NSThread currentThread]);
}
// test on <HTThread: 0x600003cb52c0>{number = 7, name = (null)}
// HTThread dealloc
开启子线程的 RunLoop 的过程
获取 RunLoop 对象
可以通过以下方式来获取RunLoop
对象:
// Foundation
[NSRunLoop mainRunLoop]; // 获取主线程的 RunLoop 对象
[NSRunLoop currentRunLoop]; // 获取当前线程的 RunLoop 对象
// Core Foundation
CFRunLoopGetMain(); // 获取主线程的 RunLoop 对象
CFRunLoopGetCurrent(); // 获取当前线程的 RunLoop 对象
我们来看一下CFRunLoopGetCurrent()
函数是怎么获取RunLoop
对象的:
CFRunLoopRef CFRunLoopGetCurrent(void) {
CHECK_FOR_FORK();
CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
if (rl) return rl;
return _CFRunLoopGet0(pthread_self()); // 调用 _CFRunLoopGet0 函数并传入当前线程
}
// should only be called by Foundation
// t==0 is a synonym for "main thread" that always works
CF_EXPORT CFRunLoopRef _CFRunLoopGet0