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

四、RunLoop的Model

五、RunLoop原理

image
1.通知Observer:要开始进入Loop
// 通知Observers
if (currentMode->_observerMask & kCFRunLoopEntry ) 
    __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
// 进入 loop
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
2.开启一个do-while循环来保活线程。通知ObserversRunLoop会触发Timer回调
// 通知 Observers RunLoop 会触发 Timer 回调
if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);

3.通知ObserversRunLoop会触发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.如果有Source1ready状态的话,就会跳转到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.回调触发后,通知ObserversRunLoop的线程将进入休眠状态
Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR);
if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);

7.进入休眠后,会等待mach_port的消息,以再次唤醒。只有在下面四个事件出现时才会被再次唤醒:
  • 基于portSource事件;
  • 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.唤醒时,通知ObserverRunLoop的线程刚刚被唤醒了
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;
}
上一篇下一篇

猜你喜欢

热点阅读