文章目录
什么是RunLoop
一般来说,一个线程一次只能执行一个任务,执行完成后线程就会退出。就比如之前学OC时使用的命令行程序,执行完程序就结束了。
而runloop,运行循环,可使线程能随时处理时间但并不退出,这也就是手机app在运行时不会退出的原因。当没有事件时,runloop会进入休眠状态,有事件发生时,runloop再进行相应的处理事件。runloop可以让线程在需要做事的时候忙起来,不需要的时候让线程休眠。
runloop基本作用
- 保持程序的持续运行
- 处理app中各种事件
- 节省CPU资源,提高程序性能:该做事时做事,该休眠时休眠。休眠时不占用CPU
runloop实际上是一个对象,这个对象管理了其需要处理的事件和消息,并提供了一个入口函数来执行下面图片中的逻辑。线程执行了这个函数后,就会处于这个函数内部的循环中,直到循环结束,函数返回。
获取runloop
iOS中有2套API来访问和使用runloop
- Foundation:NSRunLoop
- Core Foundation:CFRunLoopRef
NSRunLoop和CFRunLoopRef都代表着RunLoop对象,NSRunLoop是基于CFRunLoopRef的一层oc封装
苹果不允许直接创建runloop对象,可通过下面的方法获取runloop对象。
//获取主线程的runloop
NSRunLoop *runloop1 = [NSRunLoop mainRunLoop];
CFRunLoopRef runloop2 = CFRunLoopGetMain();
//获取当前线程的runloop对象
NSRunLoop *runloop3 = [NSRunLoop currentRunLoop];
CFRunLoopRef runloop4 = CFRunLoopGetCurrent();
下面的打印显示,虽然两种runloop打印的地址不一样,但是33行打印NSRunLoop时显示的地址却是CFRunLoop,也就说明了两种RunLoop之间的关系就如同上面图片一样,NSRunLoop是对CFRunLoop的一层oc封装。
CFRunLoopGetMain、CFRunLoopGetCurrent的实现如下
CFRunLoopRef CFRunLoopGetMain(void) {
CHECK_FOR_FORK();
static CFRunLoopRef __main = NULL; // no retain needed
if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np()); // no CAS needed
return __main;
}
CFRunLoopRef CFRunLoopGetCurrent(void) {
CHECK_FOR_FORK();
CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
if (rl) return rl;
return _CFRunLoopGet0(pthread_self());
}
这两个方法其实都是调用了_CFRunLoopGet0,_CFRunLoopGet0的实现如下
//全局的Dictionary,key为线程,value是runloop
static CFMutableDictionaryRef __CFRunLoops = NULL;
//访问loopsDic 时的锁
//锁的目的是:考虑线程安全问题,防止多条线程同时访问 __CFRunLoop对应的内存空间
static CFLock_t loopsLock = CFLockInit;
// should only be called by Foundation
// t==0 is a synonym for "main thread" that always works
//获取一个线程对应的runloop
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
//线程t为空时,获取主线程赋给t
if (pthread_equal(t, kNilPthreadT)) {
t = pthread_main_thread_np();
}
__CFLock(&loopsLock);
//如果字典为空时,也就是第一次进入
if (!__CFRunLoops) {
__CFUnlock(&loopsLock);
//创建一个临时字典dict
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
//根据主线程创建主线程对应的runloop
CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
//保存主线程,将主线程(key)和对应的runloop(value)保存到字典中
CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
//把临时字典dict赋值给全局字典中
if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
//赋值成功,释放dict
CFRelease(dict);
}
//存储mainLoop到字典后,撤销一次mainloop的引用,因为mainLoop存储到字典后会自动retain
CFRelease(mainLoop);
__CFLock(&loopsLock);
}
//从字典中根据线程(key)获取对应的runloop
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
__CFUnlock(&loopsLock);
//如果没有取到,说明字典中没有对应的loop,获取不到就创建并存储
//在子线程中第一次获取RunLoop是获取不到的,然后才去创建,这个if语句一般在子线程中第一次获取当前runloop才会进去执行
if (!loop) {
//创建一个新的runloop
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
__CFLock(&loopsLock);
//再一次从全局的字典 __CFRunLoops中获取对应于当前线程的RunLoop
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
//如果还是获取不到RunLoop,就存储当前线程和刚创建的RunLoop到全局字典 __CFRunLoops
if (!loop) {
//将新的runloop(value)和线程(key)存入全局字典中
CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
//将新的runloop赋给loop
loop = newLoop;
}
// don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it
__CFUnlock(&loopsLock);
//release局部的newloop
CFRelease(newLoop);
}
//判断t是否为当前线程,如果是就注册一个回调,当线程销毁的时候,也销毁其对应的RunLoop
if (pthread_equal(t, pthread_self())) {
_CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL);