iOS Runloop底层原理探索

iOS Runloop

[TOC]

官方文档

1. 什么是Runloop

1.1 Runloop简介

Runloop即运行循环。NSRunloopOC Foundation框架中的一个非常重要的类。

Runloop是事件接收和分发机制的一个实现,是线程相关的基础框架的一部分,一个Runloop就是一个事件处理的循环,用来不停的调度工作以及处理输入事件。这也就是为什么APP放在那里不去动它,过一会在操作它还是会给你反馈

Runloop本质是一个do while循环,但是它可以休眠,有任务就执行,没任务就休眠,但是它和普通while循环还是有区别的,普通的while循环会导致CPU进入忙等待状态,即一直消耗CPU,而Runloop不会,Runloop是一种闲等待,即Runloop具备休眠功能。

1.2 Runloop 的作用

  1. 保持程序的运行,接受用户的输入
  2. 决定程序在何时处理一些Event(触摸、定时器、performSelector)
  3. 调用解耦(message queue)
  4. 节省CPU时间,没任务的时候休眠,有任务的时候处理

1.3 依赖NSRunloop的框架

  • NSTimer
  • UIEvent
  • autorelease
  • NSObject(NSDelaydPerforming)
  • NSObject(NSThreadPerformAddtion)
  • CADisplayLink
  • CATransition
  • CAAnimation
  • dispatch_get_main_queue

1.4 Runloop消息类型

消息类型这里我们就看一下那张经典的图

image
  • port
    监听程序的Mach Ports,是一个比较底层的东西,可以简单理解为内核通过port这种方式将信息发送,而mach则监听内核发来的port信息,然后将其整理,打包发给runloop

  • Customer
    很明显,由开发人员自己发送。不仅仅是发送,过程也会相当复杂,苹果也提供了一个CFRunLoopSource来帮助处理。由于很少用到,可以简单说下核心,但是对帮助我们理解runloop却很有帮助:

    • 定义输入源(数据结构)
    • 将输入源添加到runloop,那么这样就有了接受者,即为R1
    • 协调输入源的客户端(单独线程),专门监听消息,然后将消息打包成runloop能够处理的样式,即第一步定义的输入源。它类似Mach的功能
    • 谁来发送消息的问题?上面的machport是由内核发送的。自定义的当然要我们自己发送了,首先必须是另一个线程来发送(当然如果只是测试的话可以和第三步在同一个线程),先发送消息给输入源,然后后唤醒R1,因R1一般处于休眠状态,然后R1根据输入源来做相应的处理。
  • Selector Sources:它的事件发送是同步的,这个用的比较多

  • Observers:观察者,首先它并不属于事件源(不会影响runloop的生命周期),它比较特殊,用于观察runloop自身的一些状态的,有以下几种:

    • 进入runloop
    • runloop即将执行定时器
    • runloop即将执行输入源(Port,Customer,Selector Source)
    • runloop即将休眠
    • runloop 被唤醒,在处理完唤醒它的事件之前
    • 退出

2. Runloop 探索

我们知道NSRunloop是对CFRunloop的封装,我们可以下载CFRunloop源码,下载地址,中可以下载各个版本的源码。

2.1 Runloop 与线程的关系

在日常开发中,我们通常使用以下两种方式获取Runloop

// 主运行循环
CFRunLoopRef mainRunloop = CFRunLoopGetMain();
// 当前运行循环
CFRunLoopRef currentRunloop = CFRunLoopGetCurrent();

查看一下源码:

CFRunLoopGetMain

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;
}

CFRunLoopGetCurrent

CFRunLoopRef CFRunLoopGetCurrent(void) {
    CHECK_FOR_FORK();
    CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
    if (rl) return rl;
    // 调用_CFRunLoopGet0
    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为nil,也就是没有线程,则标记为主线程,也就是默认情况都通过主线程处理
    if (pthread_equal(t, kNilPthreadT)) {
    t = pthread_main_thread_np();
    }
    __CFLock(&loopsLock);
    if (!__CFRunLoops) {
        __CFUnlock(&loopsLock);
        
    // 创建全局字典,标记为kCFAllocatorSystemDefault
    CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
    // 创建主运行循环
    CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
    // 字典的key-value绑定,这里就是主线程绑定主运行循环
    CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
    if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
        CFRelease(dict);
    }
    CFRelease(mainLoop);
        __CFLock(&loopsLock);
    }
    // 其他线程,也就是非主线程的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) {
        // 将新建的runloop与线程进行绑定,也是通过字典的key-value方式
        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有两种,一种是主线程的,另一种是其他线程的,Runloop与线程在一个全局字典中是一一对应的。

2.2 Runloop的创建

根据上一节中的代码我们可以知道,创建Runloop的方法是__CFRunLoopCreate,下面我们就看看这个方法:

static CFRunLoopRef __CFRunLoopCreate(pthread_t t) {
    CFRunLoopRef loop = NULL;
    CFRunLoopModeRef rlm;
    uint32_t size = sizeof(struct __CFRunLoop) - sizeof(CFRuntimeBase);
    // CFRunLoopRef 类型的 一个 Instance
    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;
}

2.2.1 CFRunLoopRef

通过以上代码我们可以看到loop是一个CFRunLoopRef类型,源码中主要是一些赋值操作,下面我们就看看CFRunLoopRef

typedef struct __CFRunLoop * CFRunLoopRef;

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;
};

CFRunLoopModeRef

typedef struct __CFRunLoopMode *CFRunLoopModeRef;

struct __CFRunLoopMode {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;  /* must have the run loop locked before locking this */
    CFStringRef _name;
    Boolean _stopped;
    char _padding[3];
    CFMutableSetRef _sources0;
    CFMutableSetRef _sources1;
    CFMutableArrayRef _observers;
    CFMutableArrayRef _timers;
    CFMutableDictionaryRef _portToV1SourceMap;
    __CFPortSet _portSet;
    CFIndex _observerMask;
#if USE_DISPATCH_SOURCE_FOR_TIMERS
    dispatch_source_t _timerSource;
    dispatch_queue_t _queue;
    Boolean _timerFired; // set to true by the source when a timer has fired
    Boolean _dispatchTimerArmed;
#endif
#if USE_MK_TIMER_TOO
    mach_port_t _timerPort;
    Boolean _mkTimerArmed;
#endif
#if DEPLOYMENT_TARGET_WINDOWS
    DWORD _msgQMask;
    void (*_msgPump)(void);
#endif
    uint64_t _timerSoftDeadline; /* TSR */
    uint64_t _timerHardDeadline; /* TSR */
};

从上面的结构体定义中我们可以知道,一个Runloop依赖于多个mode,意味着一个Runloop需要处理多个事物,然后一个mode有对应着多个items,而一个items中有包含了timersourceobserver。也就是下面这幅图:

image

所以Runloop的结构如下:

image

2.2.2 Mode的类型

官方文档

其中mode在苹果官方文档中体积的有五个,而在iOS中公开暴露出来的只有NSDefaultRunLoopModeNSRunLoopCommonModesNSRunLoopCommonModes实际上是一个mode的集合,默认包含NSDefaultRunLoopModeNSEventTrackingRunLoopMode

  1. NSDefaultRunLoopMode:默认的mode,一般情况下都是在这个mode
  2. NSConnectionReplyMode:处理NSConnection对象相关事件,系统内部使用,用户基本不会使用。
  3. NSModalPanelRunLoopMode:处理modal panels事件。
  4. NSEventTrackingRunLoopMode:使用这个Mode去跟着来自用户交互的事件,比如scrollView的滚动
  5. NSRunLoopCommonModes:伪模式,灵活性更好
image

2.2.3 Source & Timer & Observer

  • Source表示可以唤醒Runloop的一些事件,例如用户点击了屏幕,就会创建一个Runloop,主要分为Source0Source1
    • Source0表示非系统事件,即用户自定义的事件
    • Source1表示系统事件,主要负责底层的通讯,具备唤醒能力
  • Timer就是常用NSTimer定时器这一类
  • Observer主要用于监听Runloop的状态变化,并作出一定响应,主要有以下一些状态
/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    // 进入Runloop
    kCFRunLoopEntry = (1UL << 0),
    // 即将处理timers
    kCFRunLoopBeforeTimers = (1UL << 1),
    // 即将处理source
    kCFRunLoopBeforeSources = (1UL << 2),
    // 即将进入休眠
    kCFRunLoopBeforeWaiting = (1UL << 5),
    // 被唤醒
    kCFRunLoopAfterWaiting = (1UL << 6),
    // 退出Runloop
    kCFRunLoopExit = (1UL << 7),
    kCFRunLoopAllActivities = 0x0FFFFFFFU
};

2.2.4 验证Runloop 与 Mode 是一对多

下面我们通过代码来验证一下Runloop与Mode的关系。

  • 通过lldb命令获取mainRunloopcurrentRunloopcurrentMode
po CFRunLoopCopyCurrentMode(mainRunloop)
po CFRunLoopCopyCurrentMode(currentRunloop)
image

从这里可以看到,runloop在运行时的mode只有一个。

我们经常遇到的一个经典问题,当刷新UI的时候有时计时器是不工作的,所以就是当前modeNSEventTrackingRunLoopMode,而不是timer一开始指定的NSDefaultRunLoopMode。所以我们不改变定时器的运行模式,让他在这个模式下运行timer就不响应了,解决方法就是将定时器加入到UITrackingRunLoopModeNSRunLoopCommonModes。另外,还有一种方法就是:将timer加入到顶层的Runloop的commonModeItems中。commonModeItems被Runloop自动更新到所有具有common属性的Mode里去。

下面我们在获取一下mainRunloop的所有模型,使用po CFRunLoopCopyAllModes(mainRunloop)

image

可以看到这里是一个数组,也就说说runloopCFRunloopMode具有一对多的关系。

2.2.5 验证mode 与 item 是一对多

下面我们再来验证一下mode和item的关系,添加如下代码,添加断点,通过bt命令在lldb中打印堆栈信息

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    
    [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {

        NSLog(@"99999");
    }];
}

点击屏幕,通过bt命令查看堆栈信息:

image

在Runloop源码中可以查看Item类型,有以下几种:

image
  • GCD主队列:__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__
  • observer:__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__
  • timer:__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__
  • block:__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__
  • source0:__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
  • source1:__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__

下面我们通过一个例子来说明一下,一般初始化timer时,都会将timer通过addTimer:forMode:方法添加到runloop中,于是在源码中查找addTimer的相关方法,即CFRunLoopAddTimer方法,其源码实现如下:

void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeName) {    
    CHECK_FOR_FORK();
    if (__CFRunLoopIsDeallocating(rl)) return;
    if (!__CFIsValid(rlt) || (NULL != rlt->_runLoop && rlt->_runLoop != rl)) return;
    __CFRunLoopLock(rl);
    // 判断是否是 kCFRunLoopCommonModes 类型
    if (modeName == kCFRunLoopCommonModes) {
        // 是 kCFRunLoopCommonModes 类型
    CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
    if (NULL == rl->_commonModeItems) {
        rl->_commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
    }
        // runloop 与 mode 是一对多的,mode与item也是一对多的
    CFSetAddValue(rl->_commonModeItems, rlt);
    if (NULL != set) {
        CFTypeRef context[2] = {rl, rlt};
        /* add new item to all common-modes */
        // 执行
        CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
        CFRelease(set);
    }
    } else {
        
        // 如果不是kCFRunLoopCommonModes类型,则查找runloop的类型
    CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, true);
    if (NULL != rlm) {
            if (NULL == rlm->_timers) {
                CFArrayCallBacks cb = kCFTypeArrayCallBacks;
                cb.equal = NULL;
                rlm->_timers = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &cb);
            }
    }
        // 判断mode是否匹配
    if (NULL != rlm && !CFSetContainsValue(rlt->_rlModes, rlm->_name)) {
            __CFRunLoopTimerLock(rlt);
            if (NULL == rlt->_runLoop) {
        rlt->_runLoop = rl;
        } else if (rl != rlt->_runLoop) {
                __CFRunLoopTimerUnlock(rlt);
            __CFRunLoopModeUnlock(rlm);
                __CFRunLoopUnlock(rl);
        return;
        }
        // 如果匹配,则将runloop加进去,而runloop的执行依赖于 [runloop run]
        CFSetAddValue(rlt->_rlModes, rlm->_name);
            __CFRunLoopTimerUnlock(rlt);
            __CFRunLoopTimerFireTSRLock();
            __CFRepositionTimerInMode(rlm, rlt, false);
            __CFRunLoopTimerFireTSRUnlock();
            if (!_CFExecutableLinkedOnOrAfter(CFSystemVersionLion)) {
                // Normally we don't do this on behalf of clients, but for
                // backwards compatibility due to the change in timer handling...
                if (rl != CFRunLoopGetCurrent()) CFRunLoopWakeUp(rl);
            }
    }
        if (NULL != rlm) {
        __CFRunLoopModeUnlock(rlm);
    }
    }
    __CFRunLoopUnlock(rl);
}

根据上面的源码我们可以看到:

  • 首先判断是否是kCFRunLoopCommonModes类型
  • 是的话就正常处理
  • 不是的话就查找runloop的mode进行匹配处理
  • 其中kCFRunLoopCommonModes不是一种模式,是一种抽象的伪模式,比defauteMode更加灵活
  • 通过CFSetAddValue(rl->_commonModeItems, rlt);可以得知,runloop与mode是一对多的关系,,同时也可以得出modeitem也是一对多的关系

2.3 Runloop的执行

在日常开发中,我们都知道Runloop的执行依赖于run方法,从下面的堆栈信息中可以看出,其底层执行的是__CFRunLoopRun方法。

image

下面我们看看__CFRunLoopRun的源码(源码很多,精简的,感兴趣的可以下载看一下):

/* rl, rlm are locked on entrance and exit */
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
    ...
    
    do{
        ...
         //通知 Observers: 即将处理timer事件
        if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
        //通知 Observers: 即将处理Source事件
        if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
        //处理Blocks
        __CFRunLoopDoBlocks(rl, rlm);
        //处理sources0
        Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
        //处理sources0返回为YES
        if (sourceHandledThisLoop) {
            // 处理Blocks
            __CFRunLoopDoBlocks(rl, rlm);
        }
        
        ...
        
        //如果是timer
        else if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
            CFRUNLOOP_WAKEUP_FOR_TIMER();
            if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
                // Re-arm the next timer, because we apparently fired early
                __CFArmNextTimerInMode(rlm, rl);
            }
        }
        
        ...
        
        //如果是source1
        CFRunLoopSourceRef rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort);
        if (rls) {
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
            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);
            }
#elif DEPLOYMENT_TARGET_WINDOWS
            sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls) || sourceHandledThisLoop;
#endif
        }
        ...
    
    }while (0 == retVal);
    
    ...
}

通过源码我们可以看到,针对不同的事件,有不同的处理:

  • 如有有observer,则调用__CFRunLoopDoObservers
  • 如果有block,则调用__CFRunLoopDoBlocks
  • 如果有timer,则调用__CFRunLoopDoTimers
  • 如果有source0,则调用__CFRunLoopDoSources0
  • 如果有source1,则调用__CFRunLoopDoSource1

  • 这里我们以timer为例,看看__CFRunLoopDoTimers方法:
// rl and rlm are locked on entry and exit
static Boolean __CFRunLoopDoTimers(CFRunLoopRef rl, CFRunLoopModeRef rlm, uint64_t limitTSR) {  /* DOES CALLOUT */
    Boolean timerHandled = false;
    CFMutableArrayRef timers = NULL;
    for (CFIndex idx = 0, cnt = rlm->_timers ? CFArrayGetCount(rlm->_timers) : 0; idx < cnt; idx++) {
        CFRunLoopTimerRef rlt = (CFRunLoopTimerRef)CFArrayGetValueAtIndex(rlm->_timers, idx);
        
        if (__CFIsValid(rlt) && !__CFRunLoopTimerIsFiring(rlt)) {
            if (rlt->_fireTSR <= limitTSR) {
                if (!timers) timers = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
                CFArrayAppendValue(timers, rlt);
            }
        }
    }
    // 循环遍历执行每一个timer
    for (CFIndex idx = 0, cnt = timers ? CFArrayGetCount(timers) : 0; idx < cnt; idx++) {
        CFRunLoopTimerRef rlt = (CFRunLoopTimerRef)CFArrayGetValueAtIndex(timers, idx);
        Boolean did = __CFRunLoopDoTimer(rl, rlm, rlt);
        timerHandled = timerHandled || did;
    }
    if (timers) CFRelease(timers);
    return timerHandled;
}
  • 下面我们再看看__CFRunLoopDoTimer方法:
// mode and rl are locked on entry and exit
static Boolean __CFRunLoopDoTimer(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopTimerRef rlt) {   /* DOES CALLOUT */
    Boolean timerHandled = false;
    uint64_t oldFireTSR = 0;

    /* Fire a timer */
    CFRetain(rlt);
    __CFRunLoopTimerLock(rlt);

    if (__CFIsValid(rlt) && rlt->_fireTSR <= mach_absolute_time() && !__CFRunLoopTimerIsFiring(rlt) && rlt->_runLoop == rl) {
        void *context_info = NULL;
        void (*context_release)(const void *) = NULL;
        if (rlt->_context.retain) {
            context_info = (void *)rlt->_context.retain(rlt->_context.info);
            context_release = rlt->_context.release;
        } else {
            context_info = rlt->_context.info;
        }
        Boolean doInvalidate = (0.0 == rlt->_interval);
    __CFRunLoopTimerSetFiring(rlt);
        // Just in case the next timer has exactly the same deadlines as this one, we reset these values so that the arm next timer code can correctly find the next timer in the list and arm the underlying timer.
        rlm->_timerSoftDeadline = UINT64_MAX;
        rlm->_timerHardDeadline = UINT64_MAX;
        __CFRunLoopTimerUnlock(rlt);
    __CFRunLoopTimerFireTSRLock();
    oldFireTSR = rlt->_fireTSR;
    __CFRunLoopTimerFireTSRUnlock();

        __CFArmNextTimerInMode(rlm, rl);

    __CFRunLoopModeUnlock(rlm);
    __CFRunLoopUnlock(rl);
        // 与上面在调用堆栈中看到的方法一致
    __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(rlt->_callout, rlt, context_info);
    CHECK_FOR_FORK();
        if (doInvalidate) {
            CFRunLoopTimerInvalidate(rlt);      /* DOES CALLOUT */
        }
        if (context_release) {
            context_release(context_info);
        }
    __CFRunLoopLock(rl);
    __CFRunLoopModeLock(rlm);
        __CFRunLoopTimerLock(rlt);
    timerHandled = true;
    __CFRunLoopTimerUnsetFiring(rlt);
    }
    if (__CFIsValid(rlt) && timerHandled) {
        /* This is just a little bit tricky: we want to support calling
         * CFRunLoopTimerSetNextFireDate() from within the callout and
         * honor that new time here if it is a later date, otherwise
         * it is completely ignored. */
        if (oldFireTSR < rlt->_fireTSR) {
            /* Next fire TSR was set, and set to a date after the previous
            * fire date, so we honor it. */
            __CFRunLoopTimerUnlock(rlt);
            // The timer was adjusted and repositioned, during the
            // callout, but if it was still the min timer, it was
            // skipped because it was firing.  Need to redo the
            // min timer calculation in case rlt should now be that
            // timer instead of whatever was chosen.
            __CFArmNextTimerInMode(rlm, rl);
        } else {
        uint64_t nextFireTSR = 0LL;
            uint64_t intervalTSR = 0LL;
            if (rlt->_interval <= 0.0) {
            } else if (TIMER_INTERVAL_LIMIT < rlt->_interval) {
            intervalTSR = __CFTimeIntervalToTSR(TIMER_INTERVAL_LIMIT);
            } else {
            intervalTSR = __CFTimeIntervalToTSR(rlt->_interval);
            }
            if (LLONG_MAX - intervalTSR <= oldFireTSR) {
                nextFireTSR = LLONG_MAX;
            } else {
                if (intervalTSR == 0) {
                    // 15304159: Make sure we don't accidentally loop forever here
                    CRSetCrashLogMessage("A CFRunLoopTimer with an interval of 0 is set to repeat");
                    HALT;
                }
                uint64_t currentTSR = mach_absolute_time();
                nextFireTSR = oldFireTSR;
                while (nextFireTSR <= currentTSR) {
                    nextFireTSR += intervalTSR;
                }
            }
            CFRunLoopRef rlt_rl = rlt->_runLoop;
            if (rlt_rl) {
                CFRetain(rlt_rl);
        CFIndex cnt = CFSetGetCount(rlt->_rlModes);
        STACK_BUFFER_DECL(CFTypeRef, modes, cnt);
        CFSetGetValues(rlt->_rlModes, (const void **)modes);
        // To avoid A->B, B->A lock ordering issues when coming up
        // towards the run loop from a source, the timer has to be
        // unlocked, which means we have to protect from object
        // invalidation, although that's somewhat expensive.
        for (CFIndex idx = 0; idx < cnt; idx++) {
            CFRetain(modes[idx]);
        }
        __CFRunLoopTimerUnlock(rlt);
        for (CFIndex idx = 0; idx < cnt; idx++) {
            CFStringRef name = (CFStringRef)modes[idx];
            modes[idx] = (CFTypeRef)__CFRunLoopFindMode(rlt_rl, name, false);
            CFRelease(name);
        }
        __CFRunLoopTimerFireTSRLock();
        rlt->_fireTSR = nextFireTSR;
                rlt->_nextFireDate = CFAbsoluteTimeGetCurrent() + __CFTimeIntervalUntilTSR(nextFireTSR);
        for (CFIndex idx = 0; idx < cnt; idx++) {
            CFRunLoopModeRef rlm = (CFRunLoopModeRef)modes[idx];
            if (rlm) {
                        __CFRepositionTimerInMode(rlm, rlt, true);
            }
        }
        __CFRunLoopTimerFireTSRUnlock();
        for (CFIndex idx = 0; idx < cnt; idx++) {
            __CFRunLoopModeUnlock((CFRunLoopModeRef)modes[idx]);
        }
        CFRelease(rlt_rl);
        } else {
        __CFRunLoopTimerUnlock(rlt);
        __CFRunLoopTimerFireTSRLock();
        rlt->_fireTSR = nextFireTSR;
                rlt->_nextFireDate = CFAbsoluteTimeGetCurrent() + __CFTimeIntervalUntilTSR(nextFireTSR);
        __CFRunLoopTimerFireTSRUnlock();
            }
        }
    } else {
        __CFRunLoopTimerUnlock(rlt);
    }
    CFRelease(rlt);
    return timerHandled;
}

在上面的代码中我们可以清楚的看到__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__的调用,这与我们在调用堆栈中看到的是一致的。

所以关于timer的调用过程是这样的:

  1. 为自定义的timer设置mode,并将其加入到Runloop
  2. 在Runloop的run方法执行时,会调用__CFRunLoopDoTimers方法收集完timer后就会循环遍历执行所有timer
  3. 循环遍历的时候调用的是__CFRunLoopDoTimer方法
  4. timer执行完毕后会执行对应的回调函数

以上就是timer执行的简单分析,对于observerblocksource0source1等的指向也都大同小异,感兴趣的还可以去看看官方文档

3. Runloop 的原理

3.1 CFRunLoopRun

分析到这里我们重新回到最初的地方CFRunLoopRun,这个可以通过堆栈信息中看出,也可以从上面的分析中总结出来。

void CFRunLoopRun(void) {   /* DOES CALLOUT */
    int32_t result;
    do {
        result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
        CHECK_FOR_FORK();
    } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}

这里是一个do while循环,也就是我们一开始说的那个do while循环

这里会调用CFRunLoopRunSpecific方法,其中还有个1.0e10这就是1*10^10表示超时时间。

3.2 CFRunLoopRunSpecific

CFRunLoopRunSpecific源码如下:

SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {     /* DOES CALLOUT */
    CHECK_FOR_FORK();
    if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished;
    __CFRunLoopLock(rl);
    // 根据modeName获取到mode
    CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
    if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)) {
    Boolean did = false;
    if (currentMode) __CFRunLoopModeUnlock(currentMode);
    __CFRunLoopUnlock(rl);
    return did ? kCFRunLoopRunHandledSource : kCFRunLoopRunFinished;
    }
    volatile _per_run_data *previousPerRun = __CFRunLoopPushPerRunData(rl);
    CFRunLoopModeRef previousMode = rl->_currentMode;
    rl->_currentMode = currentMode;
    int32_t result = kCFRunLoopRunFinished;
    
    // 通知 Observers:Runloop 即将进入 loop
    if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
    // 调用 __CFRunLoopRun(真正run的地方) 进入loop
    result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
    // 通知 Observers: Runloop 即将退出
    if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);

        __CFRunLoopModeUnlock(currentMode);
        __CFRunLoopPopPerRunData(rl, previousPerRun);
    rl->_currentMode = previousMode;
    __CFRunLoopUnlock(rl);
    return result;
}

通过上面的代码我们知道这里主要有如下步骤:

  1. 根据modeName找到对应的mode
  2. run前发通知Observers即将进入loop
  3. 调用__CFRunLoopRun进行真正的run,也就是真正的进入到runloop
  4. 执行完__CFRunLoopRun后也会发生通知Observers表示Runloop即将退出

3.3 __CFRunLoopRun

再一次来到__CFRunLoopRun,下面我们看点不一样的,这里代码太多了,感兴趣的可以自行下载,下载地址。这里我们通过一份伪代码来展示一下__CFRunLoopRun的主要逻辑:

SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {     /* DOES CALLOUT */
    CHECK_FOR_FORK();
    if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished;
    __CFRunLoopLock(rl);
    
    /// 首先根据modeName找到对应mode
    CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
    
    /// 通知 Observers: RunLoop 即将进入 loop。
    __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
    
    /// 内部函数,进入loop
    result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
    
    /// 通知 Observers: RunLoop 即将退出。
    __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
    
    return result;
}

/// 核心函数
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
    
    int32_t retVal = 0;
    
    do {
        
        /// 通知 Observers: 即将处理timer事件
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
        
        /// 通知 Observers: 即将处理Source事件
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources)
        
        /// 处理Blocks
        __CFRunLoopDoBlocks(rl, rlm);
        
        /// 处理sources0
        Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
        
        /// 处理sources0返回为YES
        if (sourceHandledThisLoop) {
            /// 处理Blocks
            __CFRunLoopDoBlocks(rl, rlm);
        }
        
        
        /// 判断有无端口消息(Source1)
        if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
            /// 处理消息
            goto handle_msg;
        }
        
        /// 通知 Observers: 即将进入休眠
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
        __CFRunLoopSetSleeping(rl);
        
        /// 等待被唤醒
        __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
        
        
        // user callouts now OK again
        __CFRunLoopUnsetSleeping(rl);
        
        /// 通知 Observers: 被唤醒,结束休眠
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
        
    handle_msg:
        if (被Timer唤醒) {
            /// 处理Timers
            __CFRunLoopDoTimers(rl, rlm, mach_absolute_time());
        } else if (被GCD唤醒) {
            /// 处理gcd
            __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
        } else if (被Source1唤醒) {
            /// 被Source1唤醒,处理Source1
            __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply)
        }
        
        /// 处理block
        __CFRunLoopDoBlocks(rl, rlm);
        
        
        if (sourceHandledThisLoop && stopAfterHandle) {
            // 处理source
            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)) {
            // 什么都没有了,结束了
            retVal = kCFRunLoopRunFinished;
        }
        // 如果没超时,mode里没空,loop也没被停止,那继续loop。
    } while (0 == retVal);
    
    return retVal;
}

所以我们可以知道这里主要的逻辑就是根据不同的事件源进行不同的处理,当runloop休眠时,可以通过响应的时间唤醒Runloop

还是放一份源码吧,有点多,感兴趣的慢慢看:

/* rl, rlm are locked on entrance and exit */
/**
 *  运行run loop
 *
 *  @param rl              运行的RunLoop对象
 *  @param rlm             运行的mode
 *  @param seconds         run loop超时时间
 *  @param stopAfterHandle true:run loop处理完事件就退出  false:一直运行直到超时或者被手动终止
 *  @param previousMode    上一次运行的mode
 *
 *  @return 返回4种状态
 */

static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
    
    //获取系统启动后的CPU运行时间,用于控制超时时间
    uint64_t startTSR = mach_absolute_time();
    
    // 判断当前runloop的状态是否关闭
    if (__CFRunLoopIsStopped(rl)) {
        __CFRunLoopUnsetStopped(rl);
        return kCFRunLoopRunStopped;
    } else if (rlm->_stopped) {
        rlm->_stopped = false;
        return kCFRunLoopRunStopped;
    }
    
    //mach端口,在内核中,消息在端口之间传递。 初始为0
    mach_port_name_t dispatchPort = MACH_PORT_NULL;
    //判断是否为主线程
    Boolean libdispatchQSafe = pthread_main_np() && ((HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY && NULL == previousMode) || (!HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY && 0 == _CFGetTSD(__CFTSDKeyIsInGCDMainQ)));
    //如果在主线程 && runloop是主线程的runloop && 该mode是commonMode,则给mach端口赋值为主线程收发消息的端口
    if (libdispatchQSafe && (CFRunLoopGetMain() == rl) && CFSetContainsValue(rl->_commonModes, rlm->_name)) dispatchPort = _dispatch_get_main_queue_port_4CF();
    
#if USE_DISPATCH_SOURCE_FOR_TIMERS
    mach_port_name_t modeQueuePort = MACH_PORT_NULL;
    if (rlm->_queue) {
        //mode赋值为dispatch端口_dispatch_runloop_root_queue_perform_4CF
        modeQueuePort = _dispatch_runloop_root_queue_get_port_4CF(rlm->_queue);
        if (!modeQueuePort) {
            CRASH("Unable to get port for run loop mode queue (%d)", -1);
        }
    }
#endif
    
    dispatch_source_t timeout_timer = NULL;
    struct __timeout_context *timeout_context = (struct __timeout_context *)malloc(sizeof(*timeout_context));
    if (seconds <= 0.0) { // instant timeout
        seconds = 0.0;
        timeout_context->termTSR = 0ULL;
        // 1.0e10 == 1* 10^10
    } else if (seconds <= TIMER_INTERVAL_LIMIT) {
        //seconds为超时时间,超时时执行__CFRunLoopTimeout函数
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, DISPATCH_QUEUE_OVERCOMMIT);
        timeout_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
        dispatch_retain(timeout_timer);
        timeout_context->ds = timeout_timer;
        timeout_context->rl = (CFRunLoopRef)CFRetain(rl);
        timeout_context->termTSR = startTSR + __CFTimeIntervalToTSR(seconds);
        dispatch_set_context(timeout_timer, timeout_context); // source gets ownership of context
        dispatch_source_set_event_handler_f(timeout_timer, __CFRunLoopTimeout);
        dispatch_source_set_cancel_handler_f(timeout_timer, __CFRunLoopTimeoutCancel);
        uint64_t ns_at = (uint64_t)((__CFTSRToTimeInterval(startTSR) + seconds) * 1000000000ULL);
        dispatch_source_set_timer(timeout_timer, dispatch_time(1, ns_at), DISPATCH_TIME_FOREVER, 1000ULL);
        dispatch_resume(timeout_timer);
    } else { // infinite timeout
        //永不超时 - 永动机
        seconds = 9999999999.0;
        timeout_context->termTSR = UINT64_MAX;
    }
    
    //标志位默认为true
    Boolean didDispatchPortLastTime = true;
    //记录最后runloop状态,用于return
    int32_t retVal = 0;
    
    // itmes
 
    do {
        //初始化一个存放内核消息的缓冲池
        uint8_t msg_buffer[3 * 1024];
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
        mach_msg_header_t *msg = NULL;
        mach_port_t livePort = MACH_PORT_NULL;
#elif DEPLOYMENT_TARGET_WINDOWS
        HANDLE livePort = NULL;
        Boolean windowsMessageReceived = false;
#endif
        //取所有需要监听的port
        __CFPortSet waitSet = rlm->_portSet;
        
        //设置RunLoop为可以被唤醒状态
        __CFRunLoopUnsetIgnoreWakeUps(rl);
        
        /// 2. 通知 Observers: RunLoop 即将触发 Timer 回调。
        if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
        if (rlm->_observerMask & kCFRunLoopBeforeSources)
            /// 3. 通知 Observers: RunLoop 即将触发 Source0 (非port) 回调。
            __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
        
        /// 执行被加入的block
        __CFRunLoopDoBlocks(rl, rlm);
        /// 4. RunLoop 触发 Source0 (非port) 回调。
        Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
        if (sourceHandledThisLoop) {
            /// 执行被加入的block
            __CFRunLoopDoBlocks(rl, rlm);
        }
        
        //如果没有Sources0事件处理 并且 没有超时,poll为false
        //如果有Sources0事件处理 或者 超时,poll都为true
        Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR);
        //第一次do..whil循环不会走该分支,因为didDispatchPortLastTime初始化是true
        if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime) {
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
            //从缓冲区读取消息
            msg = (mach_msg_header_t *)msg_buffer;
            /// 5. 如果有 Source1 (基于port) 处于 ready 状态,直接处理这个 Source1 然后跳转去处理消息。
            if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0)) {
                //如果接收到了消息的话,前往第9步开始处理msg
                goto handle_msg;
            }
#elif DEPLOYMENT_TARGET_WINDOWS
            if (__CFRunLoopWaitForMultipleObjects(NULL, &dispatchPort, 0, 0, &livePort, NULL)) {
                goto handle_msg;
            }
#endif
        }
        
        didDispatchPortLastTime = false;
        /// 6.通知 Observers: RunLoop 的线程即将进入休眠(sleep)。
        if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
        //设置RunLoop为休眠状态
        __CFRunLoopSetSleeping(rl);
        // do not do any user callouts after this point (after notifying of sleeping)
        
        // Must push the local-to-this-activation ports in on every loop
        // iteration, as this mode could be run re-entrantly and we don't
        // want these ports to get serviced.
        
        __CFPortSetInsert(dispatchPort, waitSet);
        
        __CFRunLoopModeUnlock(rlm);
        __CFRunLoopUnlock(rl);
        
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
#if USE_DISPATCH_SOURCE_FOR_TIMERS
        
        //这里有个内循环,用于接收等待端口的消息
        //进入此循环后,线程进入休眠,直到收到新消息才跳出该循环,继续执行run loop
        do {
            if (kCFUseCollectableAllocator) {
                objc_clear_stack(0);
                memset(msg_buffer, 0, sizeof(msg_buffer));
            }
            
            msg = (mach_msg_header_t *)msg_buffer;
            //7.接收waitSet端口的消息
            __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY);
            //收到消息之后,livePort的值为msg->msgh_local_port,
            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));
                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);
#else
        if (kCFUseCollectableAllocator) {
            objc_clear_stack(0);
            memset(msg_buffer, 0, sizeof(msg_buffer));
        }
        msg = (mach_msg_header_t *)msg_buffer;
        /// 7. 调用 mach_msg 等待接受 mach_port 的消息。线程将进入休眠, 直到被下面某一个事件唤醒。
        /// • 一个基于 port 的Source 的事件。
        /// • 一个 Timer 到时间了
        /// • RunLoop 自身的超时时间到了
        /// • 被其他什么调用者手动唤醒
        
        // mach 事务 - 指令 
        __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY);
#endif
        
        
#elif DEPLOYMENT_TARGET_WINDOWS
        // Here, use the app-supplied message queue mask. They will set this if they are interested in having this run loop receive windows messages.
        __CFRunLoopWaitForMultipleObjects(waitSet, NULL, poll ? 0 : TIMEOUT_INFINITY, rlm->_msgQMask, &livePort, &windowsMessageReceived);
#endif
        
        __CFRunLoopLock(rl);
        __CFRunLoopModeLock(rlm);
        
        // Must remove the local-to-this-activation ports in on every loop
        // iteration, as this mode could be run re-entrantly and we don't
        // want these ports to get serviced. Also, we don't want them left
        // in there if this function returns.
        
        __CFPortSetRemove(dispatchPort, waitSet);
        
        __CFRunLoopSetIgnoreWakeUps(rl);
        
        // user callouts now OK again
        //取消runloop的休眠状态
        __CFRunLoopUnsetSleeping(rl);
        /// 8. 通知 Observers: RunLoop 的线程刚刚被唤醒了。
        if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
        
        /// 收到消息,处理消息。
    handle_msg:;
        __CFRunLoopSetIgnoreWakeUps(rl);
        
#if DEPLOYMENT_TARGET_WINDOWS
        if (windowsMessageReceived) {
            // These Win32 APIs cause a callout, so make sure we're unlocked first and relocked after
            __CFRunLoopModeUnlock(rlm);
            __CFRunLoopUnlock(rl);
            
            if (rlm->_msgPump) {
                rlm->_msgPump();
            } else {
                MSG msg;
                if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_NOYIELD)) {
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
            }
            
            __CFRunLoopLock(rl);
            __CFRunLoopModeLock(rlm);
            sourceHandledThisLoop = true;
            
            // To prevent starvation of sources other than the message queue, we check again to see if any other sources need to be serviced
            // Use 0 for the mask so windows messages are ignored this time. Also use 0 for the timeout, because we're just checking to see if the things are signalled right now -- we will wait on them again later.
            // NOTE: Ignore the dispatch source (it's not in the wait set anymore) and also don't run the observers here since we are polling.
            __CFRunLoopSetSleeping(rl);
            __CFRunLoopModeUnlock(rlm);
            __CFRunLoopUnlock(rl);
            
            __CFRunLoopWaitForMultipleObjects(waitSet, NULL, 0, 0, &livePort, NULL);
            
            __CFRunLoopLock(rl);
            __CFRunLoopModeLock(rlm);
            __CFRunLoopUnsetSleeping(rl);
            // If we have a new live port then it will be handled below as normal
        }
        
        
#endif
        if (MACH_PORT_NULL == livePort) {
            CFRUNLOOP_WAKEUP_FOR_NOTHING();
            // handle nothing
        } else if (livePort == rl->_wakeUpPort) {
            CFRUNLOOP_WAKEUP_FOR_WAKEUP();
            // do nothing on Mac OS
#if DEPLOYMENT_TARGET_WINDOWS
            // Always reset the wake up port, or risk spinning forever
            ResetEvent(rl->_wakeUpPort);
#endif
        }
#if USE_DISPATCH_SOURCE_FOR_TIMERS
        else if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
            CFRUNLOOP_WAKEUP_FOR_TIMER();
            /// 9.1 如果一个 Timer 到时间了,触发这个Timer的回调。
            if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
                // Re-arm the next timer, because we apparently fired early
                __CFArmNextTimerInMode(rlm, rl);
            }
        }
#endif
#if USE_MK_TIMER_TOO
        else if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) {
            CFRUNLOOP_WAKEUP_FOR_TIMER();
            // On Windows, we have observed an issue where the timer port is set before the time which we requested it to be set. For example, we set the fire time to be TSR 167646765860, but it is actually observed firing at TSR 167646764145, which is 1715 ticks early. The result is that, when __CFRunLoopDoTimers checks to see if any of the run loop timers should be firing, it appears to be 'too early' for the next timer, and no timers are handled.
            // In this case, the timer port has been automatically reset (since it was returned from MsgWaitForMultipleObjectsEx), and if we do not re-arm it, then no timers will ever be serviced again unless something adjusts the timer list (e.g. adding or removing timers). The fix for the issue is to reset the timer here if CFRunLoopDoTimers did not handle a timer itself. 9308754
            if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
                // Re-arm the next timer
                __CFArmNextTimerInMode(rlm, rl);
            }
        }
#endif
        /// 9.2 如果有dispatch到main_queue的block,执行block
        else if (livePort == dispatchPort) {
            CFRUNLOOP_WAKEUP_FOR_DISPATCH();
            __CFRunLoopModeUnlock(rlm);
            __CFRunLoopUnlock(rl);
            _CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)6, NULL);
#if DEPLOYMENT_TARGET_WINDOWS
            void *msg = 0;
#endif
            __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
            _CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)0, NULL);
            __CFRunLoopLock(rl);
            __CFRunLoopModeLock(rlm);
            sourceHandledThisLoop = true;
            didDispatchPortLastTime = true;
        } else {
            /// 9.3 如果一个 Source1 (基于port) 发出事件了,处理这个事件
            CFRUNLOOP_WAKEUP_FOR_SOURCE();
            // Despite the name, this works for windows handles as well
            CFRunLoopSourceRef rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort);
            if (rls) {
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
                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);
                }
#elif DEPLOYMENT_TARGET_WINDOWS
                sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls) || sourceHandledThisLoop;
#endif
            }
        }
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
        if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);
#endif
        
        /// 执行加入到Loop的block
        __CFRunLoopDoBlocks(rl, rlm);
        
        if (sourceHandledThisLoop && stopAfterHandle) {
            /// 进入loop时参数说处理完事件就返回。
            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)) {
            /// source/timer/observer一个都没有了
            retVal = kCFRunLoopRunFinished;
        }
        /// 如果没超时,mode里没空,loop也没被停止,那继续loop。
    } while (0 == retVal);
    
    if (timeout_timer) {
        dispatch_source_cancel(timeout_timer);
        dispatch_release(timeout_timer);
    } else {
        free(timeout_context);
    }
    
    return retVal;
}

综上所述,我们通过一个流程图来总结一下Runloop:

image

你可能感兴趣的:(iOS Runloop底层原理探索)