36.iOS底层学习之RunLoop

本章提纲:
1、RunLoop基础知识
2、RunLoop的数据结构
3、RunLoop的相关源码分析
4、RunLoop的实际应用

1.RunLoop基础知识

1.1RunLoop概念

参考官方文档,RunLoop的简介是这样的:

Run loops are part of the fundamental infrastructure associated with threads. A run loop is an event processing loop that you use to schedule work and coordinate the receipt of incoming events. The purpose of a run loop is to keep your thread busy when there is work to do and put your thread to sleep when there is none.

Run loop management is not entirely automatic. You must still design your thread’s code to start the run loop at appropriate times and respond to incoming events. Both Cocoa and Core Foundation provide run loop objects to help you configure and manage your thread’s run loop. Your application does not need to create these objects explicitly; each thread, including the application’s main thread, has an associated run loop object. Only secondary threads need to run their run loop explicitly, however. The app frameworks automatically set up and run the run loop on the main thread as part of the application startup process.

翻译:
RunLoop是与线程相关的基础架构的一部分。一次运行循环也就是一次事件处理循环,用于安排工作和协调接受传入的事件。RunLoop的目的是保持线程在有工作做的时候处于忙碌状态,没有事情做的时候处于休眠状态。

运行循环的管理不是完全自动的。你仍然需要设计线程代码,在合适的时间启动RunLoop以响应传入的事件。Cocoa和Core Foundation框架都提供了RunLoop对象来帮助你设置和管理你的线程RunLoop。你的应用程序不必创建这些对象;每一个线程,包括应用的主线程,都有一个关联的RunLoop对象。只有次要的线程需要运行他们的RunLoop。应用程序框架会自动的设置并且运行主线程的RunLoop,在程序开始运行起来的时候。

看了官方文档的介绍我们可以简单了解到RunLoop是:

  • 与线程密切相关;
  • 是协调和处理Event的一个循环;
  • 主线程的RunLoop不需要自己手动运行,但是次要的线程需要自己手动运行循环;
1.2RunLoop的结构

一个RunLoop和它名字是一样的。它是你的线程进入或者运行事件处理程序以响应传入事件的一个循环。你的代码提供了用于实现循环部分的控制语句,换句话说,你的代码提供了驱动运行循环的while或者for循环。在你的循环里,你可以使用一个run loop对象来运行接收到的事件的程序代码。

一个Run loop从两种不同的源接收事件。
输入源(Input)传递异步事件,通常是接收的这种消息是来自于不同应用程序的线程的。
定时器源(Timer)传递同步事件,在预定时间或重复时间间隔发生。
这两种源使用应用程序特定的handler,当事件传递进来的时候。

Runloop经典图例

根据官方文档的解释:

  • 输入源(Input)将异步事件传递给相应的程序并导致方法runUntilDate:(调用线程关联的NSRunLoop对象)退出。
  • 定时器源(Timer)将事件传递给他们的处理程序,但不会导致运行循环退出。
1.3RunLoop的Mode

一个Run Loopmode 是一个输入源和定时器可以被监视 的集合,也是Run Loop的观察者可以被通知的集合。每一次,你运行你的RunLoop,你都会在一个特定的mode下运行。在运行循环的那个过程中,只有关联这个mode的源,才允许被监视,被传递事件。(相似地,只有相关模式下的观察者才能被通知这个runloop的进程。)和其他的mode关联的源会一直持有所有的新事件,直到后续这些事件在特定的mode下通过loop。
(也就是被特定模式下的loop分发处理掉了事件,没有到相应的模式的时候对应模式的源会先持有新接到的事件,直到等到了相应的mode才去处理事件)

Cocoa 和 Core Foundation 定义的标准Mode以及何时使用该模式的描述:

Mode 名称 描述
Default NSDefaultRunLoopMode(Cocoa) kCFRunLoopDefaultMode(CoreFoundation) 默认模式是用于大多数操作的模式。大多数情况下,您应该使用此模式来启动您的运行循环并配置您的输入源。
Connection NSConnectionReplyMode (Cocoa) Cocoa 将此模式与NSConnection对象结合使用来监视回复。很少需要自己使用这种模式。
Modal NSModalPanelRunLoopMode (Cocoa) Cocoa 使用这种模式来识别用于模态面板的事件。
Event tracking NSEventTrackingRunLoopMode (Cocoa) Cocoa 使用此模式在鼠标拖动循环和其他类型的用户界面跟踪循环期间限制传入事件。
Common modes NSRunLoopCommonModes(Cocoa) kCFRunLoopCommonModes(CoreFoundation) 这是一组可配置的常用模式。将输入源与此模式相关联也会将其与组中的每个模式相关联。对于 Cocoa 应用程序,该集合默认包括默认、模态和事件跟踪模式。Core Foundation 最初仅包含默认模式。你可以使用CFRunLoopAddCommonMode功能将自定义模式添加到集合中
1.3RunLoop的循环序列

你每次运行循环,你的线程运行的循环会去处理挂起的事件,并且为已经添加的观察者生成响应,它的执行过程非常的具体:

  1. 通知观察者已经进入循环;

  2. 通知观察者所有已就绪的计时器即将被触发;

  3. 通知观察者所有不是基于端口的输入源即将被触发;

  4. 触发所有不是基于端口的输入源;

  5. 如果有基于端口的输入源已经就绪等待被触发,那么要去立即处理这个事件,跳转到步骤9;

  6. 通知观察者线程即将进入休眠;

  7. 让线程进入休眠直到出现以下几种事件发生:

  • 基于端口的输入源接收到事件
  • 计时器触发
  • RunLoop超时
  • 运行循环被显式唤醒
  1. 通知观察者线程即将苏醒;

  2. 处理挂起的事件:

  • 如果触发了用户的计时器,那么去处理计时器的事件并重新启动循环。跳转到步骤2。
  • 如果触发输入源,那么去进行事件传递
  • 如果循环被显式唤醒,但是还没有超时,重启循环,跳转到步骤2。
    10.通知观察者循环已经退出。

又是一张经典的图:


Runloop序列

经过阅读文档的一些介绍,还有参考其余资料的一些解释,初步对RunLoop有了一个认识,下面就进入到RunLoop的实际代码设计来看下它的具体结构以及源码分析。

2.Runloop的数据结构

我下载的是苹果开源的CoreFoundation源码,版本是CF-1153.18,里边关于RunLoop的部分是文件CFRunLoop,主要看的是CFRunLoop,它的数据结构如下:

struct __CFRunLoop {
    CFRuntimeBase _base;        //CF对象都有的一个东西
    pthread_mutex_t _lock;          /* locked for accessing mode list */ //锁
    __CFPort _wakeUpPort;           // used for CFRunLoopWakeUp  实际上是mach_port_t 被CFRunLoopWakeUp调用 唤醒runLoop
    Boolean _unused;
    volatile _per_run_data *_perRunData;              // reset for runs of the run loop
    pthread_t _pthread;          //对应的thread
    uint32_t _winthread;            //windows下对应的线程
    CFMutableSetRef _commonModes;   //存放 common mode 的集合
    CFMutableSetRef _commonModeItems; //每个 common mode 都有的 item (source, timer and observer) 集合
    CFRunLoopModeRef _currentMode; //当前run的mode
    CFMutableSetRef _modes;  //这个 run loop 所有的 mode 集合
    struct _block_item *_blocks_head; //链表头指针
    struct _block_item *_blocks_tail; //链表尾指针
    CFAbsoluteTime _runTime;    //总运行时间
    CFAbsoluteTime _sleepTime;  //总睡眠时间
    CFTypeRef _counterpart; //给swift使用
};

重点看下其中几个成员:

  • CFRunLoop也是一个结构体类型
  • CFRuntimeBase是CF类的一个共有的对象,是结构体结构定义如下,里边有isa,叫cfisa
typedef struct __CFRuntimeBase {
    uintptr_t _cfisa;
    uint8_t _cfinfo[4];
#if __LP64__
    uint32_t _rc;
#endif
} CFRuntimeBase;

All CF "instances" start with this structure.
这个源码的注释上说:说有的CF”实例“都起源于这个结构。

  • _wakeUpPort 用于被唤醒的时候,被CFRunLoopWakeUp调用
  • _blocks_head 链表的头指针,_block_item本身是链表结构
struct _block_item {
    struct _block_item *_next;
    CFTypeRef _mode;    // CFString or CFSet
    void (^_block)(void);
};
  • 同样的_blocks_tail表示尾结点,推测相关block的存储用的是双向链表,不然存尾结点没有意义,单向链表的尾结点指向nil,后面再去验证。
__CFRunLoopMode
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
    uint64_t _timerSoftDeadline; /* TSR */
    uint64_t _timerHardDeadline; /* TSR */
};

mode在RunLoop里很重要,了解一下它的数据结构,里面分为使用GCD的Timer和MK(__CFPort 基于 mach kernel)的Timer。

  • _timerSource: 用来实现timer的GCD timer
  • _timerPort:使用MK(__CFPort )timer的端口
  • CFRuntimeBase: 这个又出现了,前面说过CF的对象都有它,一个基本的数据,类似于isa
  • _observers: 是Observer的集合
  • _timers:Timer的集合
  • _queue :_timerSource的队列
  • _timerFired: 用来记录_timerSource是否被启动
  • _timerSoftDeadline: 下一个计划启动的时间
  • timerHardDeadline :下一个最迟启动的时间,是计划加上容忍延迟的时间
  • _name:mode的名字 唯一标识
__CFRunLoopSource
struct __CFRunLoopSource {
    CFRuntimeBase _base;
    uint32_t _bits;
    pthread_mutex_t _lock;
    CFIndex _order;         /* immutable */
    CFMutableBagRef _runLoops;
    union {
    CFRunLoopSourceContext version0;    /* immutable, except invalidation */
        CFRunLoopSourceContext1 version1;   /* immutable, except invalidation */
    } _context;
};
  • _order: 前面提到mode使用数组存储Source和Observer,根据_order添加source和observer
  • _runLoops: 对应的runLoop的集合,可以看到一个Source可以对应多个runLoop
  • 一个联合体,其中version0就是Source0,version1就是Source1,他们的具体结构如下:
    • CFRunLoopSourceContext:Source0
    typedef struct {
        CFIndex   version;
        void *    info;
        const void *(*retain)(const void *info);
        void  (*release)(const void *info);
        CFStringRef   (*copyDescription)(const void *info);
        Boolean   (*equal)(const void *info1, const void *info2);
        CFHashCode    (*hash)(const void *info);
     // schedule cancel 是对应的,
        void  (*schedule)(void *info, CFRunLoopRef rl, CFStringRef mode);
        void  (*cancel)(void *info, CFRunLoopRef rl, CFStringRef mode);
    
        void  (*perform)(void *info); //回调的指针
    } CFRunLoopSourceContext;
    
    • CFRunLoopSourceContext1:Source1
    typedef struct {
      CFIndex version;
      void *  info;
      const void *(*retain)(const void *info);
      void    (*release)(const void *info);
      CFStringRef (*copyDescription)(const void *info);
      Boolean (*equal)(const void *info1, const void *info2);
      CFHashCode  (*hash)(const void *info);
    #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) || (TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)
      mach_port_t (*getPort)(void *info);
      void *  (*perform)(void *msg, CFIndex size, CFAllocatorRef allocator, void *info);
    #else
      void *  (*getPort)(void *info);
      void    (*perform)(void *info); //回调的指针
    #endif
    } CFRunLoopSourceContext1;
    

可以看到Source1Source0多个接收消息的端口mach_port_tSource0中有schedulecancel来处理时间段的事件。

__CFRunLoopObserver
struct __CFRunLoopObserver {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;
    CFRunLoopRef _runLoop;
    CFIndex _rlCount;
    CFOptionFlags _activities;      /* immutable */
    CFIndex _order;         /* immutable */
    CFRunLoopObserverCallBack _callout; /* immutable */
    CFRunLoopObserverContext _context;  /* immutable, except invalidation */
};
  • _runLoop:observer对应的runLoop, 一一对应
  • _rlCount:observer当前监测的runLoop数量,主要在安排/移除runLoop的时候用到
  • _activities:observer观测runLoop的状态,枚举类型
/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0), //即将进入Loop
    kCFRunLoopBeforeTimers = (1UL << 1), //runLoop即将处理 Timers
    kCFRunLoopBeforeSources = (1UL << 2),//runLoop即将处理 Sources
    kCFRunLoopBeforeWaiting = (1UL << 5), //runLoop即将进入休眠
    kCFRunLoopAfterWaiting = (1UL << 6), //runLoop刚从休眠中唤醒
    kCFRunLoopExit = (1UL << 7), //退出Loop
    kCFRunLoopAllActivities = 0x0FFFFFFFU
};
  • _order:前面的Source中也有,记录在mode集合中的位置
  • _callout:观察到情况的回调
__CFRunLoopTimer
struct __CFRunLoopTimer {
    CFRuntimeBase _base;
    uint16_t _bits;
    pthread_mutex_t _lock;
    CFRunLoopRef _runLoop;
    CFMutableSetRef _rlModes;
    CFAbsoluteTime _nextFireDate;
    CFTimeInterval _interval;       /* immutable */
    CFTimeInterval _tolerance;          /* mutable */
    uint64_t _fireTSR;          /* TSR units */
    CFIndex _order;         /* immutable */
    CFRunLoopTimerCallBack _callout;    /* immutable */
    CFRunLoopTimerContext _context; /* immutable, except invalidation */
};
  • _runLoop:timer对应的runLoop
  • _rlModes:存放所有 包含该timer的 mode的 modeName,意味着一个timer可能会在多个mode中存在
  • _nextFireDate:下一次timer启动的时间
  • _interval:理想的时间间隔
  • _tolerance:容忍度,时间误差

了解完以上比较重要的数据结构我们来做一下小结,总结一下runLoop、mode、timer、source、observer之间的关系:
1、在RunLoop中mode是一个集合,也就是说一个RunLoop可以有多种mode,只有一个_pthread,和_pthread是一对一的关系

2、在Mode中分别有timer、source、observe,他们都是集合,说明都可以有多个

3、在Timer中mode也是一个集合类型,说明一个Timer也可以对应多种mode,可以放到不同的mode中,但是runLoop只有一个,是一对一的关系

4、在Source中,定义的RunLoop是一个集合,也就是说Source和RunLoop也是一对多的关系

5、在observer中,它和RunLoop是一一对应的关系
他们的部分关系图解如下:

关系图

3.Runloop的相关源码分析

了解完相关的数据结构,再来看看部分代码的具体实现。

3.1CFRunLoopAddTimer

此方法是添加定时器Timer时最终调用到的方法。

void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeName) {    
    CHECK_FOR_FORK();
    //如果rl对象正在释放 直接返回
    if (__CFRunLoopIsDeallocating(rl)) return;
    //不存在直接返回
    if (!__CFIsValid(rlt) || (NULL != rlt->_runLoop && rlt->_runLoop != rl)) return;
    //加锁
    __CFRunLoopLock(rl);
    //commonMode的情况 拿到相应的set
    if (modeName == kCFRunLoopCommonModes) {
        CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
        //为空 创建modeItems
        if (NULL == rl->_commonModeItems) {
            rl->_commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
        }
        CFSetAddValue(rl->_commonModeItems, rlt);
        //rl->_commonModes存在 添加新的item到commonModes
        if (NULL != set) {
            CFTypeRef context[2] = {rl, rlt};
        /* add new item to all common-modes */
            CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
            CFRelease(set);
        }
    //非commonMode的情况
    } else {
        
        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);
            }
        }
        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;
        }
            //将timer添加到对应的mode中
        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);
}

主要是处理CommonMode非CommonMode情况下的Timer,添加到不同的Mode下。
我们暂停程序运行的时候,查看主线程堆栈可以看到:

image.png

调用了CFRunLoopRunSpecific,查看源码可以看到CFRunLoopRun中调用了该方法。

3.2 CFRunLoopRun
//供外部调用的CFRunLoopRun方法
void CFRunLoopRun(void) {   /* DOES CALLOUT */
    int32_t result;
    do {
        //默认情况是在kCFRunLoopDefaultMode 下工作
        //1.0e10 科学计数法表示 1*10的10次方
        //除非停止或者结束 不然会一直循环
        result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
        CHECK_FOR_FORK();
    } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}

方法CFRunLoopRun内部是一个do-while结构,当状态为stop或者finished的时候就会退出循环,RunLoop结束,线程会被回收。
1.0e10是科学计数法,表示1乘以10的10次方,传入这个值表示线程常驻。
do-while中调用了方法CFRunLoopRunSpecific它的实现内容挺多,省略掉一部分在其他架构下的代码。

CFRunLoopRunSpecific
//内部调用 __CFRunLoopRun
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {     /* DOES CALLOUT */
    
    CHECK_FOR_FORK();
    
    if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished;
    __CFRunLoopLock(rl);
    //获取当前Mode 去找到当前Mode
    //如果有则返回找到的
    //如果没有,且不需要创建 返回NULL
    //如果没有,需要创建,则新建一个Mode
    CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
    //判断mode的状态是不是合法 不合法返回
    if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)) {
            Boolean did = false;
        if (currentMode) __CFRunLoopModeUnlock(currentMode);
        __CFRunLoopUnlock(rl);
        return did ? kCFRunLoopRunHandledSource : kCFRunLoopRunFinished;
    }
    
    //因为运行嵌套使用 把上一次的data先push存储起来
    volatile _per_run_data *previousPerRun = __CFRunLoopPushPerRunData(rl);
    
    CFRunLoopModeRef previousMode = rl->_currentMode;
    rl->_currentMode = currentMode;
    int32_t result = kCFRunLoopRunFinished;

    //通知 observers:RunLoop 要开始进入 loop 了。紧接着就进入 loop。
    if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
    //执行__CFRunLoopRun
    result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
    //退出
    if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);

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

此方法大致分为两步:
1、处理Mode,对Mode的获取通过方法__CFRunLoopFindMode,拿到之后进一步去校验Mode的状态,是不是空,如果不是能继续进行的Mode直接返回;
2、通知observers要进入RunLoop了,执行__CFRunLoopRun,通知observers要退出RunLoop了。

接下来进一步看关键的执行过程__CFRunLoopRun,代码非常多,用伪代码来说明下它的内部主要执行过程。

__CFRunLoopRun
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
    
    int32_t retVal = 0;
    // 1.通知即将进入runloop
    __CFRunLoopDoObservers(KCFRunLoopEntry);
    do {  // itmes do
        
        /// 2.通知 Observers: 即将处理timer事件
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
        
        /// 3.通知 Observers: 即将处理Source事件
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources)
        
        /// 处理Blocks
        __CFRunLoopDoBlocks(rl, rlm);
        
        /// 4.处理sources0
        Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
        
        /// 处理sources0返回为YES
        if (sourceHandledThisLoop) {
            /// 处理Blocks
            __CFRunLoopDoBlocks(rl, rlm);
        }

        /// 5.判断有无端口消息(Source1)
        if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
            /// 如果有Source1,跳到第9步,处理消息
            goto handle_msg;
        }
        
        /// 6.通知 Observers: 即将进入休眠
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
        __CFRunLoopSetSleeping(rl);
        
        /// 7.等待被唤醒
        __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);

        // user callouts now OK again
        __CFRunLoopUnsetSleeping(rl);
        
        /// 8.通知 Observers: 被唤醒,结束休眠
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
    
    /// 9.处理唤醒时收到的消息
    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) {
            /// 进入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);
    
    return retVal;
}

这个过程正好对应了上面那个官方文档的流程。
这里有一些对Block的存储和回调处理,这些回调被存到了runLoop.block_head指向的链表中,进行调用时会遍历链表,如果block所处的mode和当前匹配会调用__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__

image.png

网上找到一张图,描述的挺好的:
__CFRunLoopRun

个人理解RunLoop

经过这些源码的查看和网络资料的借鉴,自己对RunLoop有了初步的一些小理解,可能不够准确或者有问题,希望评论区指正,再做修改。

如果用仿生学的角度去看RunLoop,把它比作一个人的话,那么它要去处理内核态和用户态两种情况下接受的一些消息。

在整个程序的运行期间,"心脏"要一直跳动也就是不能处于"死亡"的状态,所以它需要是一个常驻线程,一直存活。

为了减少整个程序运行的负荷,这个RunLoop有这样的特点,没事情做的时候要去”睡觉“,去休息。有事情做的时候会被叫醒来工作。

人的眼睛耳朵就相当于source1的端口Port,来接受来自于外界的输入信息,也就是其他进程的消息。

大脑内部有一些每天定时要去做的事情,比如起床,吃饭,也就是通过timer去驱动的一些事件,到了时间runLoop去发消息让对应的线程去做相应的工作。

所有的工作都会有相应的回调,形成一个完整的闭环。比如timer驱动的事件,”吃饭“,那么”吃完饭“会有一个回调,来记录事件是不是被处理完毕,完毕了要从列表中移除掉,一个runLoop下来没处理完,要等下一次runLoop,被处理的mode要和当前的mode一致才能被处理。

一次事件处理完毕,可以继续处理事件,也可以到吃完饭就结束了,没有接下来的事情所做。如果在接下来都没事情做,那么runLoop就被设置成beforeWaiting的状态,要进入休眠休息了。

所有的工作就这样循环往复的被处理,整个系统就被运行起来了。
(感觉有点勉强哈哈哈,为了加深印象和理解写了这么一段)。

4.Runloop的实际应用

在AFNetWorking中的使用

AFURLConnectionOperation是基于NSURLConnection构建的,其希望能在后台线程接收Delegate的回调。为此AFNetWorking专门开启了一个线程,并在这个线程中启动了一个RunLoop:

+ (void)networkRequestThreadEntryPoint:(id)__unused object {
    @autoreleasepool {
        [[NSThread currentThread] setName:@"AFNetworking"];
        NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
        [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
        [runLoop run];
    }
}
 
+ (NSThread *)networkRequestThread {
    static NSThread *_networkRequestThread = nil;
    static dispatch_once_t oncePredicate;
    dispatch_once(&oncePredicate, ^{
        _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
        [_networkRequestThread start];
    });
    return _networkRequestThread;
}

RunLoop启动前必须至少有一个Timer/Source/Observer,所以在run之前创建了一个新的NSMachPort (mach_port),让loop不退出。当后台需要这个线程执行任务时,通过调用[NSObject performSelector:onThread:..]将任务扔到后台的线程中进行执行。

- (void)start {
    [self.lock lock];
    if ([self isCancelled]) {
        [self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
    } else if ([self isReady]) {
        self.state = AFOperationExecutingState;
        [self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
    }
    [self.lock unlock];
}

苹果对于RunLoop的使用还是非常的广泛,例如AutoreleasePool、手势识别、事件响应、UI更新、定时器等方面都有使用RunLoop,他是整个程序能够运行的基础,后面有机会会进行更加深入的学习和研究。

你可能感兴趣的:(36.iOS底层学习之RunLoop)