RunLoop概述
一般来讲,一个线程一次只能执行一个任务,执行完成后线程就会退出。如果我们需要一个机制,让线程能随时处理事件但并不退出,通常的代码逻辑是这样的:
Event loop
RunLoop和Thread
-
RunLoop和线程的关系:
一个RunLoop对应着一条唯一的线程
-
RunLoop的创建:
MainThread上的RunLoop默认是创建好并启动的,其它Thread中的RunLoop默认是没有创建RunLoop
- RunLoop的生命周期:
在第一次获取时创建,在线程结束时销毁。除了MainThread外,其它Thread首次获取该Thread的RunLoop时才被创建,代码流程见下面
// 全局的 Dictionary ,key 是 pthread_t , value 是 CFRunLoopRef
static CFMutableDictionaryRef loopsDic;
// 访问 loopsDic 时的线程锁
static CFSpinLock_t loopsLock;
// 获取一个 pthread 对应的 RunLoop
CFRunLoopRef _CFRunLoopGet(pthread_t thread) {
OSSpinLockLock(&loopsLock);
if (!loopsDic) {
// 第一次进入时,初始化全局 Dic ,并先为主线程创建一个RunLoop
loopsDic = CFDictionaryCreateMutable();
CFRunLoopRef mainLoop = _CFRunLoopCreate();
CFDictionarySetValue(loopsDic, pthread_main_thread_np(), mainLoop);
}
// 直接从 Dictionary 里获取 thread
CFRunLoopRef loop = CFDictionaryGetValue(loopsDis, thread);
if (!loop) {
// 如果取不到,创建一个
loop = _CFRunLoopCreate();
CFDictionarySetValue(loopsDic, thread, loop);
// 注册一个回调,当线程销毁时,顺便也销毁其对应的 RunLoop
_CFSetTSD(..., thread, loop, __CFFinalizeRunLoop);
}
OSSpinLockUnLock(&loopsLock);
return loop;
}
从上面的代码可以看出,线程和RunLoop之间是一一对应的,其关系是保存在一个全局的Dictionary里。线程刚创建时并没有RunLoop,如果你不主动获取,那它一直都不会被创建。RunLoop的创建是发生在第一次获取时,RunLoop的销毁是发生在线程结束时。只能在一个线程的内部获取其RunLoop(MainThread除外)。
注意:
- 开一个子线程创建RunLoop,不是通过alloc init方法创建,而是直接通过调用currentRunLoop方法来创建,它本身是一个懒加载的。
- RunLoop对象是利用字典来进行存储的,而且key是对应的Thread,value为该Thread对应的RunLoop
- CFRunLoopRef源码可以在CoreFoundation源码中进行了解