这是我参与8月更文挑战的第25天,活动详情查看:8月更文挑战
什么是RunLoop
从字面上理解就是一个运行循环,一般程序就是执行一个线程,是一条直线,有起点和终点。而RunLoop就是一直在线程上面画圆圈,一直在跑圈,在不断跑圈中,一直在检测一些点击事件、定时器等等,一旦检测到就开始执行,执行结束后再睡眠,睡眠中再检测,除非切断否则一直在运行,否则就一直在循环。其内部的结构是一个do-while循环,在这个循环内部不断处理各种任务(比如timer、source、observer)
RunLoop基本作用
- 保持程序的持续运行,App持续运行就是因为有RunLoop
- 处理App中的各种事件(比如触摸事件、定时事件、Selector事件)
- 节省CPU资源,提高程序性能,该做事时做事,该休息时休息
RunLoop对象
NSRunLoop 和 CFRunLoopRef 都代表着 RunLoop 对象,NSRunLoop 是基于 CFRunLoopRef 的一层 OC 的包装
- Foundation : NSRunLoop
- CoreFoundation : CFRunLoopRef
RunLoop与线程
- 每条线程都有唯一的一个与之相对应的 RunLoop 对象
- 主线程的 RunLoop 系统已经创建好了,子线程的 RunLoop 需要自己创建
- RunLoop 在第一次获取时创建,在线程结束时销毁
//Foundation
[NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象
[NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象
//Core Foundation
CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象
CFRunLoopGetMain(); // 获得主线程的RunLoop对象
注:
NSRunLoop 初始化不需要 alloc ,只需要在该线程里调用[NSRunLoop currentRunLoop]
苹果不允许直接创建 RunLoop,它只提供了两个自动获取的函数:CFRunLoopGetMain() 和 CFRunLoopGetCurrent()。 这两个函数内部的逻辑大概是下面这样:
// 全局的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 里获取。
CFRunLoopRef loop = CFDictionaryGetValue(loopsDic, thread));
if (!loop) {
// 取不到时,创建一个
loop = _CFRunLoopCreate();
CFDictionarySetValue(loopsDic, thread, loop);
// 注册一个回调,当线程销毁时,顺便也销毁其对应的 RunLoop。
_CFSetTSD(..., thread, loop, __CFFinalizeRunLoop);
}
OSSpinLockUnLock(&loopsLock);
return loop;
}
CFRunLoopRef CFRunLoopGetMain() {
return _CFRunLoopGet(pthread_main_thread_np());
}
CFRunLoopRef CFRunLoopGetCurrent() {
return _CFRunLoopGet(pthread_self());
}
从上面的代码可以看出,线程和 RunLoop 之间是一一对应的,其关系是保存在一个全局的 Dictionary 里。线程刚创建时并没有 RunLoop,如果你不主动获取,那它一直都不会有。RunLoop 的创建是发生在第一次获取时,RunLoop 的销毁是发生在线程结束时。你只能在一个线程的内部获取其 RunLoop(主线程除外)。