iOS -- RunLoop源码分析
2019-04-09 本文已影响0人
Q海龙
一、什么是RunLoop?
Run Loop翻译过来差不多就是运行着的循环
。它的作用本质也可以用Run
代表。那Run
什么呢?Run
线程,使线程保持活跃状态,而线程主线程与子线程,也许在日常开发中,并没有在线程中看到RunLoop
,因为这些操作都是在Objective-C底层帮你处理好了。
二、解读RunLoop
苹果不允许直接创建RunLoop
,它只是提供了两个只读属性
//获取当前线程`RunLoop`
@property (class, readonly, strong) NSRunLoop *currentRunLoop;
//获取主线程`RunLoop`
@property (class, readonly, strong) NSRunLoop *mainRunLoop API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
// 获得主线程对应的 RunLoop对象
NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop];
// 获得主线程对应的 RunLoop对象
NSRunLoop *mainRunLoop = [NSRunLoop mainRunLoop];
接下来看下底层是如何获取RunLoop对象的
CFRunLoop关键代码
CFRunLoopRef CFRunLoopGetMain(void) {
CHECK_FOR_FORK();
//静态变量保存主线程的RunLoop对象
static CFRunLoopRef __main = NULL; // no retain needed
//如果__main不存在,则调用_CFRunLoopGet0方法获取CFRunLoopRef对象并返回
if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np()); // no CAS needed
return __main;
}
CFRunLoopRef CFRunLoopGetCurrent(void) {
CHECK_FOR_FORK();
//获取当前线程的CFRunLoopRef对象
CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
if (rl) return rl;
//rl为空,则调用_CFRunLoopGet0方法创建当前线程的CFRunLoopRef对象
return _CFRunLoopGet0(pthread_self());
}
// should only be called by Foundation
// t==0 is a synonym for "main thread" that always works
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
//线程t为空
if (pthread_equal(t, kNilPthreadT)) {
//t = 主线程
t = pthread_main_thread_np();
}
//锁,防止创建多个RunLoop对象
__CFLock(&loopsLock);
if (!__CFRunLoops) {
__CFUnlock(&loopsLock);
//存放RunLoop对象的字典
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
//根据主线程创建RunLoop对象
//所以,当第一次获取RunLoop对象时就会自动创建主线程关联的RunLoop对象
CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
//存储到字典中,Key为主线程指针对象,Value为对应的RunLoop对象
CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
CFRelease(dict);
}
CFRelease(mainLoop);
__CFLock(&loopsLock);
}
//通过线程t,获取对应的RunLoop对象
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
__CFUnlock(&loopsLock);
if (!loop) {
//如果RunLoop不存在,则创建一个
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
__CFLock(&loopsLock);
//再获取一次
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
if (!loop) {
//如果还是没找到,则将刚才创建的newLoop存到字典中,并loop = newLoop
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())) {
//注册一个回调,回调方法为 __CFFinalizeRunLoop,当线程销毁时,顺便也销毁其对应的 RunLoop。
_CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL);
if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) {
_CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop);
}
}
return loop;
}
static CFRunLoopRef __CFRunLoopCreate(pthread_t t) {
CFRunLoopRef loop = NULL;
CFRunLoopModeRef rlm;
uint32_t size = sizeof(struct __CFRunLoop) - sizeof(CFRuntimeBase);
loop = (CFRunLoopRef)_CFRuntimeCreateInstance(kCFAllocatorSystemDefault, CFRunLoopGetTypeID(), size, NULL);
if (NULL == loop) {
return NULL;
}
(void)__CFRunLoopPushPerRunData(loop);
__CFRunLoopLockInit(&loop->_lock);
loop->_wakeUpPort = __CFPortAllocate();
if (CFPORT_NULL == loop->_wakeUpPort) HALT;
__CFRunLoopSetIgnoreWakeUps(loop);
loop->_commonModes = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
CFSetAddValue(loop->_commonModes, kCFRunLoopDefaultMode);
loop->_commonModeItems = NULL;
loop->_currentMode = NULL;
loop->_modes = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
loop->_blocks_head = NULL;
loop->_blocks_tail = NULL;
loop->_counterpart = NULL;
loop->_pthread = t;
#if DEPLOYMENT_TARGET_WINDOWS
loop->_winthread = GetCurrentThreadId();
#else
loop->_winthread = 0;
#endif
rlm = __CFRunLoopFindMode(loop, kCFRunLoopDefaultMode, true);
if (NULL != rlm) __CFRunLoopModeUnlock(rlm);
return loop;
}
总结一下上面的代码:
线程和
RunLoop
之间是一一对应的,其关系保存在一个全局的字典里(__CFRunLoops)
。线程刚创建时并没有RunLoop
,如果不主动获取,那它就不会有。而它的创建是在第一次获取时,销毁则是在线程结束时。只能在线程内部获取其RunLoop
,主线程除外。主线程的RunLoop
是一直运行的,RunLoop在执行完任务后会进入休眠,等待下一次启动。
RunLoop
与线程的关系:
- 一个线程可以有一个
RunLoop
,也可能没有- 主线程的
RunLoop
已经自动创建好,而子线程的RunLoop
需要主动创建RunLoop
在第一次获取时创建,在线程结束时销毁
三、RunLoop的组成
Timer、Source0、Source1、Observer
-
Time
:定时器触发 -
Source0
:处理App内部时间,App自己负责触发(UIEvent
、CFSocket
) -
Source1
:由RunLoop
和mach
内核管理,由mach-port
驱动 -
Observer
:由Observer
触发,如CAAnimation
,在afterwaiting
收集完所有animation
后才执行动画
四、RunLoop的Model
-
NSDefaultRunLoopModel
(KCFRunLoopDefaultModel
):App默认的Model,通常主线程是在这个Model下运行的 -
UITrackingRunLoopModel
:界面跟踪Model,用于ScrollView
追踪触摸滑动,保证界面滑动时不受其它Model影响 -
UIInitializationRunLoopMode
:在刚启动App时进入的第一个Model,启动完成后就不再使用 -
NSRunLoopCommonModes
(kCFRunLoopCommonModes
):Model集合,其包含NSDefaultRunLoopModel
和UITrackingRunLoopModel
-
GSEventReceiveRunLoopMode
:接受系统事件内部Model,通常用不到
五、RunLoop原理
image1.通知Observer
:要开始进入Loop
了
// 通知Observers
if (currentMode->_observerMask & kCFRunLoopEntry )
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
// 进入 loop
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
2.开启一个do-while
循环来保活线程。通知Observers
:RunLoop
会触发Timer
回调
// 通知 Observers RunLoop 会触发 Timer 回调
if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
3.通知Observers
:RunLoop
会触发Source0
回调,接着执行加入的block
// 通知 Observers RunLoop 会触发 Source0 回调
if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
// 执行 block
__CFRunLoopDoBlocks(rl, rlm);
4.触发Source0
回调
Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
if (sourceHandledThisLoop) {
__CFRunLoopDoBlocks(rl, rlm);
}
5.如果有Source1
是ready
状态的话,就会跳转到handle_msg
去处理消息
if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime) {
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
msg = (mach_msg_header_t *)msg_buffer;
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
goto handle_msg;
}
#elif DEPLOYMENT_TARGET_WINDOWS
if (__CFRunLoopWaitForMultipleObjects(NULL, &dispatchPort, 0, 0, &livePort, NULL)) {
goto handle_msg;
}
#endif
}
6.回调触发后,通知Observers
:RunLoop
的线程将进入休眠状态
Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR);
if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
7.进入休眠后,会等待mach_port
的消息,以再次唤醒。只有在下面四个事件出现时才会被再次唤醒:
- 基于
port
的Source
事件;Timer
时间到RunLoop
超时- 被调用者唤醒
do {
if (kCFUseCollectableAllocator) {
// objc_clear_stack(0);
// <rdar://problem/16393959>
memset(msg_buffer, 0, sizeof(msg_buffer));
}
msg = (mach_msg_header_t *)msg_buffer;
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
//基于Port的Source事件、调用者唤醒
if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
// Drain the internal queue. If one of the callout blocks sets the timerFired flag, break out and service the timer.
while (_dispatch_runloop_root_queue_perform_4CF(rlm->_queue));
//Timer时间到、RunLoop超时
if (rlm->_timerFired) {
// Leave livePort as the queue port, and service timers below
rlm->_timerFired = false;
break;
} else {
if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);
}
} else {
// Go ahead and leave the inner loop.
break;
}
} while (1);
8.唤醒时,通知Observer
:RunLoop
的线程刚刚被唤醒了
if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
9.RunLoop
被唤醒后就要开始处理消息了
- 如果是
Timer
时间到,就触发Timer
的回调- 如果是
dispatch
的话,就执行block
- 如果是
Source1
事件的话,就处理这个事件
消息执行完后,就执行加到loop
里的block
handle_msg:
// 如果 Timer 时间到,就触发 Timer 回调
if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
// Re-arm the next timer, because we apparently fired early
__CFArmNextTimerInMode(rlm, rl);
}
// 如果 dispatch 就执行 block
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
// Source1 事件的话,就处理这个事件
mach_msg_header_t *reply = NULL;
sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop;
if (NULL != reply) {
(void)mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
CFAllocatorDeallocate(kCFAllocatorSystemDefault, reply);
}
10.根据当前RunLoop
的状态来判断是否需要走下一个loop
。当被外部强制停止或loop
超时时,就不继续下一个loop
了,否则继续下一个loop
if (sourceHandledThisLoop && stopAfterHandle) {
//事件已处理完
retVal = kCFRunLoopRunHandledSource;
} else if (timeout_context->termTSR < mach_absolute_time()) {
//超时
retVal = kCFRunLoopRunTimedOut;
} else if (__CFRunLoopIsStopped(rl)) {
//外部调用者强制停止
__CFRunLoopUnsetStopped(rl);
retVal = kCFRunLoopRunStopped;
} else if (rlm->_stopped) {
rlm->_stopped = false;
retVal = kCFRunLoopRunStopped;
} else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
//mode为空,RunLoop结束
retVal = kCFRunLoopRunFinished;
}