iOS底层 之 RunLoop攻坚
什么是RunLoop
顾名思义,RunLoop就是一个保证程序或者线程持续运行的循环。
runloop 作用
- 保持程序持续运行。
- 处理各种事件 :触摸事件、定时器时间、selector事件、网络请求等
- 节省CPU资源,提高程序性能:该干活儿时干活儿,该休息时休息
每一个线程,只对应唯一一个Runloop
iOS开发中,UIApplicationMain 函数启动了主线程的 主RunLoop
,保证程序的主线程的运行状态,这样UI就不会卡顿。主线程中的runloop是系统主动开启的,开发者手动开启的子线程内的runloop如果不手动开启,子线程执行完就结束了,线程就销毁了。所以,要想手动开启的主线程执行完任务后不被销毁,就需要手动启动RunLoop
.
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
RunLoop的五个模式
kCFRunLoopDefaultMode
:APP默认的模式,通常主线程就是在这个模式下运行UITrackingRunLoopMode
: 界面跟踪模式,用于scrollView跟踪触摸滚动,保证滚动时候 不受其他线程影响kCFRunLoopCommonModes
: 占位用的mode,不是一个真正的modeNSRunLoopCommonModes
: 相当于NSDefaultRunLoopMode
+UITrackingRunLoopMode
UIInitializationRunLoopMode
: 刚启动APP时,进入的第一个mode启动完后不再使用GSEventReceiveRunLoopMode
:接受系统事件的内部mode,通常用不到
模式使用总结:
- 一个RunLoop包含多个mode,每个mode包含若干source0、source1、observer、timer
- RunLoop 只能选择一个mode作为currentRunLoop的mode。
- 如果想要切换mode,需要先退出当前mode,再重新选择一个mode进入
- 不同组的Source、timer、observer 互不影响
- 如果Mode里面没有任何的source、timer、observer, RunLoop会立马退出,
RunLoop 对象
iOS开发中有两套API来访问和使用RunLoop
- < Foundation >:
NSRunLoop
- < Core Foundation >:
CFRunLoopRef
NSRunLoop 和 CFRunLoopRef 都代表RunLoop对象。 NSRunLoop 是基于CFRunLoopRef的一层OC封装。
获取RunLoop 的 API 使用:
Foundation
[NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象
[NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象
Core Foundation
CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象
CFRunLoopGetMain(); // 获得主线程的RunLoop对象
RunLoop 和 线程之间的关系
- 每个线程都有对应一个唯一的RunLoop对象
- RunLoop保存在一个全局键值对里,线程作为key,RunLoop作为value
- 主线程的RunLoop系统创建并启动好了,子线程的RunLoop需要手动创建
- RunLoop在第一次获取时创建,在线程结束时销毁
- 线程创建时候,并没有RunLoop对象,所以,执行完代码线程就销毁了。第一次获取线程对象时候创建RunLoop对象,这时候才会循环等待,保证线程不结束,不被销毁。
- RunLoop会在线程结束时销毁
RunLoop源码
RunLoop 结构体
struct __CFRunLoop {
CFRuntimeBase _base;
pthread_mutex_t _lock; /* locked for accessing mode list */
__CFPort _wakeUpPort; // used for CFRunLoopWakeUp
Boolean _unused;
volatile _per_run_data *_perRunData; // reset for runs of the run loop
pthread_t _pthread;
uint32_t _winthread;
CFMutableSetRef _commonModes;
CFMutableSetRef _commonModeItems;
CFRunLoopModeRef _currentMode;
CFMutableSetRef _modes;
struct _block_item *_blocks_head;
struct _block_item *_blocks_tail;
CFAbsoluteTime _runTime;
CFAbsoluteTime _sleepTime;
CFTypeRef _counterpart;
};
runloop 内部实现源码
// 拿到当前Runloop 调用_CFRunLoopGet0
CFRunLoopRef CFRunLoopGetCurrent(void) {
CHECK_FOR_FORK();
CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
if (rl) return rl;
return _CFRunLoopGet0(pthread_self());
}
// 查看_CFRunLoopGet0方法内部
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
if (pthread_equal(t, kNilPthreadT)) {
t = pthread_main_thread_np();
}
__CFLock(&loopsLock);
if (!__CFRunLoops) {
__CFUnlock(&loopsLock);
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);
if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
CFRelease(dict);
}
CFRelease(mainLoop);
__CFLock(&loopsLock);
}
// 从字典里面拿,将线程作为key从字典里获取一个loop
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
__CFUnlock(&loopsLock);
// 如果loop为空,则创建一个新的loop,所以runloop会在第一次获取的时候创建
if (!loop) {
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
__CFLock(&loopsLock);
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
// 创建好之后,以线程为key runloop为value,一对一存储在键值对中,下次获取的时候,则直接返回字典内的runloop
if (!loop) {
CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
loop = newLoop;
}
// don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it
__CFUnlock(&loopsLock);
CFRelease(newLoop);
}
if (pthread_equal(t, pthread_self())) {
_CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL);
if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) {
_CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop);
}
}
return loop;
}
RunLoop 中的五个类
- CFRunLoopRef
- CFRunLoopModeRef
- CFRunLoopSourceRef
- CFRunLoopTimerRef
- CFRunLoopObserverRef
CFRunLoopActivity
/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), // 即将进入RunLoop
kCFRunLoopBeforeTimers = (1UL << 1), // 即将处理Timer
kCFRunLoopBeforeSources = (1UL << 2), // 即将处理source
kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠
kCFRunLoopAfterWaiting = (1UL << 6), // 唤醒后
kCFRunLoopExit = (1UL << 7), // 即将推出RunLoop
kCFRunLoopAllActivities = 0x0FFFFFFFU
};
监听RunLoop的所有状态
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
switch (activity) {
case kCFRunLoopEntry:
{// 刚进入RunLoop
}
break;
case kCFRunLoopBeforeTimers:
{// 即将处理timer
}
break;
case kCFRunLoopBeforeSources:
{// 即将处理source
}
break;
case kCFRunLoopBeforeWaiting:
{// 即将进入休眠
}
break;
case kCFRunLoopAfterWaiting:
{// 唤醒后
}
break;
case kCFRunLoopExit:
{// 即将退出RunLoop
}
break;
default:
break;
}
});
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopCommonModes);
CFRelease(observer);