RunLoop 在 0202 年的今天其实已经不是个新鲜的话题了,关于这方面的文章网上有很多大神总结得非常精辟。

作为 iOS 查漏补缺系列,这篇文章是笔者探索 RunLoop 底层的一些知识点总结,同时也借鉴了网上一些优秀的 RunLoop 技术文章的内容。

本文内容如有错误,欢迎指正。

RunLoop 前导知识

iOS/OS X 系统架构

iOS 进化史

Mac OS Classic 拥有伟大的 GUI,但系统设计却非常糟糕,尤其是协作式的多任务系统和低效的内存管理,以今天的标准看非常原始。

NeXTSTEP 操作系统作为乔布斯回归苹果的嫁妆,连同架构中的 Mach 和 OC 等一起融合进了Mac OS Classic。与其说是 Mac OS Classic 融合了 NeXTSTEP,不如说是后者反客为主替换了前者,这个转变并不是一瞬间发生的。

Mac OS Classic 拥有不错的 GUI 但系统设计糟糕,NeXTSTEP 设计很棒,但 GUI 非常平淡。这两个小众操作系统融合的结果是一个继承二者优点的成功得多的全新操作系统—Mac OS X
全新的 Mac OS X 在设计与实现上都同 NeXTSTEP 非常接近,诸如 Cocoa、Mach 、Interface Builder 等核心组件都源自 NeXTSTEP

iOS 最初称为 iPhone OS,是 Mac OS X 应对移动平台的分支,iOS 拥有和 Mac OS X一样的操作系统层次结构以及相同的操作系统核心Dawin。

iOS 系统架构

苹果官方将整个系统大致划分为上述4个层次:

应用层包括用户能接触到的图形应用,例如 Spotlight、Aqua(macOS)、SpringBoard(iOS) 等。

应用框架层即开发人员接触到的 Cocoa 等框架。

核心框架层包括各种核心框架、OpenGL 等内容。

Darwin层是操作系统核心,包括 XNU 内核(2017 年已开源)、驱动和 UNIX shell

Darwin 架构

Darwin 的内核是 XNU,XNU is Not Unix。XNU 是两种技术的混合体,Mach和BSD。BSD 层确保了 Darwin 系统的 UNIX 特性,真正的内核是 Mach,但是对外部隐藏。BSD 以上属于用户态,所有的内容都可以被应用程序访问,而应用程序不能访问内核态。当需要从用户态切换到内核态的时候,需要通过 mach trap 实现切换。

—《深入解析Mac OS X & iOS 操作系统》

其中,在硬件层上面的三个组成部分:Mach、BSD、IOKit (还包括一些上面没标注的内容),共同组成了 XNU 内核。

XNU 内核的内环被称作 Mach,其作为一个微内核,仅提供了诸如处理器调度、IPC (进程间通信)等非常少量的基础服务。

BSD 层可以看作围绕 Mach 层的一个外环,其提供了诸如进程管理、文件系统和网络等功能。

IOKit 层是为设备驱动提供了一个面向对象(C++)的一个框架。

Mach 本身提供的 API 非常有限,而且苹果也不鼓励使用 Mach 的 API,但是这些API非常基础,如果没有这些API的话,其他任何工作都无法实施。
在 Mach 中,所有的东西都是通过自己的对象实现的,进程、线程和虚拟内存都被称为”对象”。

和其他架构不同, Mach 的对象间不能直接调用,只能通过消息传递的方式实现对象间的通信。

”消息”是 Mach 中最基础的概念,消息在两个端口 (port) 之间传递,这就是 Mach 的 IPC (进程间通信) 的核心。

— 《深入理解RunLoop》ibireme

mach_msg 函数

mach_msg 函数位于 mach 内核的 message.h 头文件中

这是系统内核在某个 port 收发消息所使用的函数,理解这个函数对于理解 runloop 的运行机制非常重要。
详细的说明可参考 这里。

// mach 消息基础结构体
typedef struct{// 消息头部mach_msg_header_t       header;// 消息体mach_msg_body_t         body;
} mach_msg_base_t;// mach 消息头结构体
typedef struct{// 消息头的 bitsmach_msg_bits_t       msgh_bits;// 消息头的大小mach_msg_size_t       msgh_size;// 远程端口号mach_port_t           msgh_remote_port;// 本地端口号mach_port_t           msgh_local_port;// 凭证端口号mach_port_name_t      msgh_voucher_port;// 消息IDmach_msg_id_t         msgh_id;
} mach_msg_header_t;// mach_msg 函数
mach_msg_return_t mach_msg(// 消息头mach_msg_header_t *msg,// 消息选项,可用来指定是发消息(MACH_SEND_MSG)还是收消息(MACH_RCV_MSG)mach_msg_option_t option,// 发消息时设置的缓存区大小,无符号整型mach_msg_size_t send_size,// 收消息时设置的缓存区大小,无符号整型mach_msg_size_t rcv_size,// 收消息的端口,无符号整型mach_port_name_t rcv_name,// 消息过期时间// 当 option 参数中包含 MACH_SEND_TIMEOUT 或 MACH_RCV_TIMEOUT 的时候,传入以毫秒单位的过期时间mach_msg_timeout_t timeout,// 接收通知的端口// 当 option 参数中包含 MACH_SEND_CANCEL 和 MACH_RCV_NOTIFY 时,设置接收通知的端口;否则,传入 MACH_PORT_NULL。mach_port_name_t notify);

可以简单的将 mach_msg 理解为多进程之间的一种通信机制,不同的进程可以使用同一个消息队列来交流数据,当使用 mach_msg 从消息队列里读取 msg 时,可以在参数中设置 timeout 值,在 timeout 之前如果没有读到 msg,当前线程会一直处于休眠状态。这也是 runloop 在没有任务可执行的时候,能够进入 sleep 状态的原因。
— 《解密 Runloop》MrPeak

为了实现消息的发送和接收,mach_msg() 函数实际上是调用了一个 Mach 陷阱 (trap),即函数 mach_msg_trap(),陷阱这个概念在 Mach 中等同于系统调用。当你在用户态调用 mach_msg_trap() 时会触发陷阱机制,切换到内核态;内核态中内核实现的 mach_msg() 函数会完成实际的工作。
— 《深入理解RunLoop》ibireme

CoreFoundation 基础

CoreFoundation 是一套基于 CAPI,为 iOS 提供了基本数据管理和服务功能。因为 CoreFoundation 对象是基于 C 实现的,也有引用计数的概念,而 Foundation 对象是基于 OC 实现的。两种对象在相互转换的时候需要用到三个关键字: **bridge、**bridge_retained、__bridge_transfer,下面进行简单的介绍。

__bridge

id obj = [[NSObject alloc] init];
void *p = (__bridge void *)obj;
id o = (__bridge id)p;

将 Objective-C 的对象类型用 *bridge 转换为 void 类型和使用 **unsafe_unretained 关键字修饰的变量是一样的。

__bridge: 只做类型转换,不修改相关对象的引用计数,原来的 Core Foundation 对象在不用时,需要调用 CFRelease 方法

__bridge_retained

d obj = [[NSObject alloc] init];
void *p = (__bridge_retained void *)obj;

从名字上我们应该能理解其意义:类型被转换时,其对象的所有权也将被变换后变量所持有。
如果是 MRC 中:

id obj = [[NSObject alloc] init];
void *p = obj;
[(id)p retain];

下面的例子验证了出了大括号的范围后,p 仍然指向一个有效的实体。说明她拥有该对象的所有权,该对象没有因为出其定义范围而被销毁。

void *p = 0;
{id obj = [[NSObject alloc] init];p = (__bridge_retained void *)obj;
}
NSLog(@"class=%@", [(__bridge id)p class]);

__bridge_retained: 类型转换后,将相关对象的引用计数加 1,原来的 Core Foundation 对象在不用时,需要调用 CFRelease 方法

__bridge_transfer

MRC 中:

// p 变量原先持有对象的所有权
id obj = (id)p;
[obj retain];
[(id)p release];

ARC 中:

// p 变量原先持有对象的所有权
id obj = (__bridge_transfer id)p;

可以看出来,**bridge_retained 是编译器替我们做了 retain 操作,而 **bridge_transfer 是替我们做了 release。

__bridge_transfer: 类型转换后,将该对象的引用计数交给 ARC 管理,Core Foundation 对象在不用时,不需要调用 CFRelease 方法

Toll-Free bridged

CoreFoundation 和 Foundation 之间有一个 Toll-free bridging 机制。
在 MRC 时代,可以直接通过类型强转完成两个框架对象的类型转换。但是在 ARC 时代,则需要使用 Toll-free bridging 机制提供的方法来操作。上面已经介绍了三种不同的转换机制,而实际上在 Foundation 中,有更简便的方法来操作。如下所示:

// After using a CFBridgingRetain on an NSObject, the caller must take responsibility for calling CFRelease at an appropriate time.
NS_INLINE CF_RETURNS_RETAINED CFTypeRef _Nullable CFBridgingRetain(id _Nullable X) {return (__bridge_retained CFTypeRef)X;
}NS_INLINE id _Nullable CFBridgingRelease(CFTypeRef CF_CONSUMED _Nullable X) {return (__bridge_transfer id)X;
}

CFBridgingRetain 可以代替 **bridge_retained,CFBridgingRelease 可以代替 **bridge_transfer。

RunLoop 初探

什么是 RunLoop

根据苹果官方文档的定义

Run loops are part of the fundamental infrastructure associated with threads.
RunLoop 是与线程相关的基础结构。

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.
RunLoop 是一个调度任务和处理事件的事件循环。其意义在于让线程有事做的时候繁忙起来,没事做的时候休眠。

了解过 NodeJS 事件循环机制的同学应该不会对 事件循环 这一名词陌生,用伪代码实现如下:

    while(AppIsRunning) {id whoWakesMe = SleepForWakingUp();id event = GetEvent(whoWakesMe);HandleEvent(event);}

这份伪代码来自 sunnyx 大神的 RunLoop 技术分享

可以看到,RunLoop 就是这么一个简单的工作原理。但是其内部实现要比上面的代码复杂得多。我们下一节将深入 RunLoop 底层一探究竟。

OSX/iOS 系统中,提供了两个这样的对象:NSRunLoop 和 CFRunLoopRef。

CFRunLoopRef 是在 CoreFoundation 框架内的,它提供了纯 C 函数的 API,所有这些 API 都是线程安全的。

NSRunLoop 是基于 CFRunLoopRef 的封装,提供了面向对象的 API,但是这些 API 不是线程安全的。

RunLoop 底层数据结构

如上图所示,每一个 RunLoop 内部有多个 mode,而每一个 mode 内部有 sources0,source1, observers 和 timers。mode 中的元素统称为 mode item。下面我们一起回顾下这五个类的数据结构定义:

  • CFRunLoopRef
  • CFRunLoopModeRef
  • CFRunLoopSourceRef
  • CFRunLoopTimerRef
  • CFRunLoopObserverRef

CFRunLoopRef

typedef struct __CFRunLoop * CFRunLoopRef;struct __CFRunLoop
{// CoreFoundation 中的 runtime 基础信息CFRuntimeBase _base;// 针对获取 mode 列表操作的锁pthread_mutex_t _lock; /* locked for accessing mode list */// 唤醒端口__CFPort _wakeUpPort;  // used for CFRunLoopWakeUp// 是否使用过Boolean _unused;// runloop 运行会重置的一个数据结构volatile _per_run_data *_perRunData; // reset for runs of the run loop// runloop 所对应线程pthread_t _pthread;uint32_t _winthread;// 存放 common mode 的集合CFMutableSetRef _commonModes;// 存放 common mode item 的集合CFMutableSetRef _commonModeItems;// runloop 当前所在 modeCFRunLoopModeRef _currentMode;// 存放 mode 的集合CFMutableSetRef _modes;// runloop 内部 block 链表表头指针struct _block_item *_blocks_head;// runloop 内部 block 链表表尾指针struct _block_item *_blocks_tail;// 运行时间点CFAbsoluteTime _runTime;// 休眠时间点CFAbsoluteTime _sleepTime;CFTypeRef _counterpart;
};// 每次 RunLoop 运行后会重置
typedef struct _per_run_data
{uint32_t a;uint32_t b;uint32_t stopped;   // runloop 是否停止uint32_t ignoreWakeUps; // runloop 是否已唤醒
} _per_run_data;// 链表节点
struct _block_item
{// 指向下一个 _block_itemstruct _block_item *_next;// 要么是 string 类型,要么是集合类型,也就是说一个 block 可能对应单个或多个 modeCFTypeRef _mode; // CFString or CFSet// 存放的真正要执行的 blockvoid (^_block)(void);
};

通过上面的代码可以看出,CFRunLoopRef 其实是一个 __CFRunLoop 类型的结构体指针。__CFRunLoop 内部以下属性需要重点关注:

  • _pthread
  • _commonModes
  • _currentMode
  • _modes

CFRunLoopModeRef

typedef struct __CFRunLoopMode *CFRunLoopModeRef;struct __CFRunLoopMode
{// CoreFoundation 中的 runtime 基础信息CFRuntimeBase _base;// 互斥锁,加锁前需要 runloop 先加锁pthread_mutex_t _lock; /* must have the run loop locked before locking this */// mode 的名称CFStringRef _name;// mode 是否停止Boolean _stopped;char _padding[3];// source0CFMutableSetRef _sources0;// source1CFMutableSetRef _sources1;// observersCFMutableArrayRef _observers;// timersCFMutableArrayRef _timers;CFMutableDictionaryRef _portToV1SourceMap;// port 的集合__CFPortSet _portSet;// observer 的 maskCFIndex _observerMask;// 如果定义了 GCD 定时器
#if USE_DISPATCH_SOURCE_FOR_TIMERS// GCD 定时器dispatch_source_t _timerSource;// 队列dispatch_queue_t _queue;// 当 GCD 定时器触发时设置为 trueBoolean _timerFired; // set to true by the source when a timer has firedBoolean _dispatchTimerArmed;
#endif
// 如果使用 MK_TIMER
#if USE_MK_TIMER_TOO// MK_TIMER 的 portmach_port_t _timerPort;Boolean _mkTimerArmed;
#endif
#if DEPLOYMENT_TARGET_WINDOWSDWORD _msgQMask;void (*_msgPump)(void);
#endif// 定时器软临界点uint64_t _timerSoftDeadline; /* TSR */// 定时器硬临界点uint64_t _timerHardDeadline; /* TSR */
};

通过上面的代码可以看出,CFRunLoopModeRef 其实是一个 __CFRunLoopMode 类型的结构体指针。
__CFRunLoopMode 内部以下属性需要重点关注:

  • _sources0
  • _sources1
  • _observers
  • _timers

CFRunLoopSourceRef

typedef struct __CFRunLoopSource * CFRunLoopSourceRef;struct __CFRunLoopSource
{// CoreFoundation 中的 runtime 基础信息CFRuntimeBase _base;uint32_t _bits;// 互斥锁pthread_mutex_t _lock;// source 的优先级,值为小,优先级越高CFIndex _order; /* immutable */// runloop 集合CFMutableBagRef _runLoops;// 一个联合体,说明 source 要么为 source0,要么为 source1union {CFRunLoopSourceContext version0;  /* immutable, except invalidation */CFRunLoopSourceContext1 version1; /* immutable, except invalidation */} _context;
};typedef struct {CFIndex version;// source 的信息void *  info;const void *(*retain)(const void *info);void    (*release)(const void *info);CFStringRef (*copyDescription)(const void *info);// 判断 source 相等的函数Boolean (*equal)(const void *info1, const void *info2);CFHashCode  (*hash)(const void *info);void    (*schedule)(void *info, CFRunLoopRef rl, CFStringRef mode);void    (*cancel)(void *info, CFRunLoopRef rl, CFStringRef mode);// source 要执行的任务块void    (*perform)(void *info);
} CFRunLoopSourceContext;

通过上面的代码可以看出,CFRunLoopSourceRef 其实是一个 __CFRunLoopSource 类型的结构体指针。
__CFRunLoopSource 的结构相对来说要简单一点,有几个属性要重点关注:

  • _order
  • _context
  • CFRunLoopSourceContext 下的 perform 函数指针

CFRunLoopTimerRef

typedef struct CF_BRIDGED_MUTABLE_TYPE(NSTimer) __CFRunLoopTimer * CFRunLoopTimerRef;struct __CFRunLoopTimer
{// CoreFoundation 中的 runtime 基础信息CFRuntimeBase _base;uint16_t _bits;// 互斥锁pthread_mutex_t _lock;// timer 对应的 runloopCFRunLoopRef _runLoop;// timer 对应的 mode 集合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 */
};typedef struct {CFIndex    version;void *    info;const void *(*retain)(const void *info);void  (*release)(const void *info);CFStringRef   (*copyDescription)(const void *info);
} CFRunLoopTimerContext;typedef void (*CFRunLoopTimerCallBack)(CFRunLoopTimerRef timer, void *info);

通过上面的代码可以看出,CFRunLoopTimerRef 其实是一个 __CFRunLoopTimer 类型的结构体指针,并且这里 __CFRunLoopTimer 在前面还有一个 CF_BRIDGED_MUTABLE_TYPE(NSTimer)

#define CF_BRIDGED_TYPE(T)      __attribute__((objc_bridge(T)))
#define CF_BRIDGED_MUTABLE_TYPE(T)  __attribute__((objc_bridge_mutable(T)))
#define CF_RELATED_TYPE(T,C,I)      __attribute__((objc_bridge_related(T,C,I)))
#else
#define CF_BRIDGED_TYPE(T)
#define CF_BRIDGED_MUTABLE_TYPE(T)
#define CF_RELATED_TYPE(T,C,I)
#endif

从这里说明 CFRunLoopTimerRefNSTimertoll-free bridged 的。

__CFRunLoopTimer 中有几个属性需要重点关注:

  • _order
  • _callout
  • _context

CFRunLoopObserverRef

typedef struct __CFRunLoopObserver * CFRunLoopObserverRef;struct __CFRunLoopObserver
{// CoreFoundation 中的 runtime 基础信息CFRuntimeBase _base;// 互斥锁pthread_mutex_t _lock;// observer 对应的 runloopCFRunLoopRef _runLoop;// observer 观察了多少个 runloopCFIndex _rlCount;CFOptionFlags _activities;          /* immutable */// observer 优先级CFIndex _order;                     /* immutable */// observer 回调函数CFRunLoopObserverCallBack _callout; /* immutable */// observer 上下文CFRunLoopObserverContext _context;  /* immutable, except invalidation */
};typedef struct {CFIndex    version;void *    info;const void *(*retain)(const void *info);void  (*release)(const void *info);CFStringRef   (*copyDescription)(const void *info);
} CFRunLoopObserverContext;typedef void (*CFRunLoopObserverCallBack)(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info);

通过上面的代码可以看出,CFRunLoopObserverRef 其实是一个 __CFRunLoopObserver 类型的结构体指针。

__CFRunLoopObserver 中有几个属性需要重点关注:

  • _order
  • _callout
  • _context

CFRunLoopActivity

/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {kCFRunLoopEntry = (1UL << 0),kCFRunLoopBeforeTimers = (1UL << 1),kCFRunLoopBeforeSources = (1UL << 2),kCFRunLoopBeforeWaiting = (1UL << 5),kCFRunLoopAfterWaiting = (1UL << 6),kCFRunLoopExit = (1UL << 7),kCFRunLoopAllActivities = 0x0FFFFFFFU
};

通过上面的源码可知,RunLoop 有以下状态

  • kCFRunLoopEntry: 即将进入 Loop
  • kCFRunLoopBeforeTimers: 即将处理 Timer
  • kCFRunLoopBeforeSources: 即将处理 Source
  • kCFRunLoopBeforeWaiting: 即将进入休眠
  • kCFRunLoopAfterWaiting: 刚从休眠中唤醒
  • kCFRunLoopExit: 即将推出 Loop

RunLoop 与线程关系

要想搞清楚 RunLoop 与线程的关系,我们从 CFRunLoopGetMainCFRunLoopGetCurrent 函数开始研究:

CFRunLoopRef CFRunLoopGetMain(void)
{CHECK_FOR_FORK();// 声明一个空的 RunLoopstatic CFRunLoopRef __main = NULL; // no retain needed// 如果 __main 为空,则调用 _CFRunLoopGet0 函数来获取 RunLoopif (!__main)__main = _CFRunLoopGet0(pthread_main_thread_np()); // no CAS neededreturn __main;
}CFRunLoopRef CFRunLoopGetCurrent(void)
{CHECK_FOR_FORK();// 从 线程的 TSD(Thread Specific Data) 中获取 runloopCFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);// 如果拿到了就返回if (rl)return rl;// 如果在 TSD 中没有,那么就调用 _CFRunLoopGet0 函数return _CFRunLoopGet0(pthread_self());
}

pthread_main_thread_np() 获取的是主线程,其内部实现如下

#define pthread_main_thread_np() _CF_pthread_main_thread_np()

CF_EXPORT pthread_t _CF_pthread_main_thread_np(void);
pthread_t _CF_pthread_main_thread_np(void) {
return _CFMainPThread;
}

void __CFInitialize(void) {

_CFMainPThread = pthread_self();


}

我们接着探索 _CFRunLoopGet0 函数,其实现如下

// 存储了 runloop 与线程的字典
static CFMutableDictionaryRef __CFRunLoops = NULL;
static CFLock_t loopsLock = CFLockInit;// should only be called by Foundation
// t==0 is a synonym for "main thread" that always works
// t为0的话,表示是要获取 主线程对应的 RunLoop
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t)
{// 如果传入的现场为空,则再通过 pthread_main_thread_np() 获取到主线程if (pthread_equal(t, kNilPthreadT)){t = pthread_main_thread_np();}// 加锁__CFLock(&loopsLock);// 如果 __CFRunLoops 字典为空,则初始化字典,并把 mainLoop 加入字典if (!__CFRunLoops){// 解锁__CFUnlock(&loopsLock);// 初始化一个 可变字典CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);// 拿到主线程对应的 RunLoopCFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());// 将 runloop 和 线程存入字典中,线程为 key,runloop 为 valueCFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void *volatile *)&__CFRunLoops)){// 释放 字典CFRelease(dict);}// 释放 mainLoopCFRelease(mainLoop);// 加锁__CFLock(&loopsLock);}// 根据传入的 线程 t 从字典中获取 loop 对象CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));// 对 loops 字典解锁__CFUnlock(&loopsLock);// 如果 loop 为空if (!loop){// 创建一个新的 loopCFRunLoopRef newLoop = __CFRunLoopCreate(t);// 对 loops 字典加锁__CFLock(&loopsLock);// 从 loops 字典中获取 looploop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));if (!loop){// 如果在字典中没有找到 loop,把新的 loop 存入 loops 字典中CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);loop = newLoop;}// don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it// 不要在 字典锁作用域里面释放 runloop 对象,因为 CFRunLoopDeallocate 会释放掉 runloop__CFUnlock(&loopsLock);// 释放 newLoopCFRelease(newLoop);}// 判断 t 是否是当前线程if (pthread_equal(t, pthread_self())){// 设置 thread specific data,简称 TSD,这也是一个字典// 以 __CFTSDKeyRunLoop 为 key, loop 对象为 value 存入 TSD 中/**// Set some thread specific data in a pre-assigned slot. Don't pick a random value.Make sure you're using a slot that is unique.Pass in a destructor to free this data, or NULL if none is needed. Unlike pthread TSD, the destructor is per-thread.// 和 pthread 的 TSD 不一样的时,第三个参数 析构函数指针是每个线程都拥有的CF_EXPORT void *_CFSetTSD(uint32_t slot, void *newVal, void (*destructor)(void *));*/_CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL);if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)){_CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS - 1), (void (*)(void *))__CFFinalizeRunLoop);}}return loop;
}

通过上面的源码,我们明确了在底层有一个字典的数据结构。
这个字典存储方式的是以 pthread_t 线程为 key,CFRunLoopRef 为 value。
我们可以有以下的结论:

  • 每条线程都有唯一的一个与之对应的RunLoop对象
  • RunLoop 保存在一个全局的 Dictionary 里,线程作为 key,RunLoop 作为 value
  • 线程刚创建时并没有 RunLoop 对象,RunLoop 会在第一次获取它时创建
  • RunLoop 会在线程结束时销毁
  • 主线程的 RunLoop 已经自动获取(创建),子线程默认没有开启RunLoop

Runloop 底层

RunLoop 启动方式

void CFRunLoopRun(void)
{ /* DOES CALLOUT */int32_t result;do{result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);CHECK_FOR_FORK();} while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}SInt32 CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled)
{ /* DOES CALLOUT */CHECK_FOR_FORK();return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled);
}

由上面的源码可知:

  • 默认启动方式,底层是通过 CFRunLoopRun 来实现,超时时间传入的是 1.0e10,这是一个很大的数字,所以可以理解为不超时。
  • 自定义启动方式,可以配置超时时间以及 mode 等其它参数。

RunLoop 核心逻辑

我们接着进入 CFRunLoopRunSpecific:

SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled)
{ /* DOES CALLOUT */CHECK_FOR_FORK();// 如果 runloop 正在回收中,直接返回 kCFRunLoopRunFinished ,表示 runloop 已经完成if (__CFRunLoopIsDeallocating(rl))return kCFRunLoopRunFinished;// 对 runloop 加锁__CFRunLoopLock(rl);// 从 runloop 中查找给定的 modeCFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);// 如果找不到 mode,且当前 runloop 的 currentMode 也为空,进入 if 逻辑// __CFRunLoopModeIsEmpty 函数结果为空的话,说明 runloop 已经处理完所有任务if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)){Boolean did = false;// 如果 currentMode 不为空if (currentMode)// 对 currentMode 解锁__CFRunLoopModeUnlock(currentMode);// 对 runloop 解锁__CFRunLoopUnlock(rl);return did ? kCFRunLoopRunHandledSource : kCFRunLoopRunFinished;}// 暂时取出 runloop 的 per_run_datavolatile _per_run_data *previousPerRun = __CFRunLoopPushPerRunData(rl);// 取出 runloop 的当前 modeCFRunLoopModeRef previousMode = rl->_currentMode;// 将查找到的 mode 赋值到 runloop 的 _curentMode,也就是说在这 runloop 完成了 mode 的切换rl->_currentMode = currentMode;// 初始化返回结果 resultint32_t result = kCFRunLoopRunFinished;// 如果注册了 observer 监听 kCFRunLoopEntry 状态(即将进入 loop),则通知 observerif (currentMode->_observerMask & kCFRunLoopEntry)__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);// runloop 核心函数 __CFRunLoopRunresult = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);// 如果注册了 observer 监听 kCFRunLoopExit 状态(即将推出 loop),则通知 observerif (currentMode->_observerMask & kCFRunLoopExit)__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);// 对 currentMode 解锁__CFRunLoopModeUnlock(currentMode);// 还原原来的 previousPerRun__CFRunLoopPopPerRunData(rl, previousPerRun);// 还原原来的 moderl->_currentMode = previousMode;// 对 runloop 解锁__CFRunLoopUnlock(rl);return result;
}

这块的代码精简一下之后:

SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled)
{ /* DOES CALLOUT */// 通知 Observers 进入 loop__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);// 具体要做的事情result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);// 通知 Observers 退出 loop__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);return result;
}

功夫不负有心人,终于来到了最核心的流程 __CFRunLoopRun 函数,说白了,一次运行循环就是一次 __CFRunLoopRun 的运行。

static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode)

__CFRunLoopRun 参数有 5 个,分别表示:

  • rl: CFRunLoopRef 对象
  • rlm: mode 的名称
  • seconds: 超时时间
  • stopAfterHandle: 处理完 source 后是否直接返回
  • previousMode: 前一次运行循环的 mode

下面提供 __CFRunLoopRun 的完整代码和精简代码,读者可根据自身需要选择阅读。

__CFRunLoopRun 完整代码

static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode)
{// 获取开始时间uint64_t startTSR = mach_absolute_time();if (__CFRunLoopIsStopped(rl)){// Runloop 处于停止状态,修改runloop底层数据结构 停止状态 flag__CFRunLoopUnsetStopped(rl);// 返回 kCFRunLoopRunStopped 状态return kCFRunLoopRunStopped;}else if (rlm->_stopped){// Mode 处于停止状态,修改 mode 的成员变量 _stopped 为 falserlm->_stopped = false;// 返回 kCFRunLoopRunStopped 状态return kCFRunLoopRunStopped;}// 判断是否有主队列的 Mach Portmach_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)));if (libdispatchQSafe && (CFRunLoopGetMain() == rl) && CFSetContainsValue(rl->_commonModes, rlm->_name))dispatchPort = _dispatch_get_main_queue_port_4CF();#if USE_DISPATCH_SOURCE_FOR_TIMERS// 从 mode 的 成员变量 _queue 中取出对应的 Mach Portmach_port_name_t modeQueuePort = MACH_PORT_NULL;if (rlm->_queue){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// 声明一个空的 GCD 定时器dispatch_source_t timeout_timer = NULL;// 初始化一个 「超时上下文」 结构体指针对象struct __timeout_context *timeout_context = (struct __timeout_context *)malloc(sizeof(*timeout_context));// 如果 runloop 超时时间小于等于0if (seconds <= 0.0){ // instant timeout// 将 seconds 置为 0sseconds = 0.0;// 将 「超时上下文」的 termTSR 属性设置为 0,0ULL 后面的 ULL 表示 Unsigned Long Long 即无符号 long long 整型timeout_context->termTSR = 0ULL;}else if (seconds <= TIMER_INTERVAL_LIMIT){// 如果 runloop 超时时间小于等于 TIMER_INTERVAL_LIMIT 常量// 如果主线程存在,则获取主线程的主队列// 如果主线程不存在,则获取全局队列dispatch_queue_t queue = pthread_main_np() ? __CFDispatchQueueGetGenericMatchingMain() : __CFDispatchQueueGetGenericBackground();// 将队列传入并初始化一个 GCD 对象,对象类型为定时器timeout_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);// 对 GCD 对象进行 retaindispatch_retain(timeout_timer);// 将 GCD 对象赋值于 「超时上下文」 结构体的 ds 属性timeout_context->ds = timeout_timer;// 对 runloop 对象进行 retain,然后赋值于「超时上下文」 结构体的 rl 属性timeout_context->rl = (CFRunLoopRef)CFRetain(rl);// 将本次运行循环的开始时间加上要运行的时间得到runloop的生命周期时间// 然后赋值于「超时上下文」 结构体的 termTSR 属性timeout_context->termTSR = startTSR + __CFTimeIntervalToTSR(seconds);// 设置 GCD 对象的上下文为 自定义的 「超时上下文」 结构体dispatch_set_context(timeout_timer, timeout_context); // source gets ownership of context// 设置 GCD 对象的回调 可以理解为要执行的任务/**static void __CFRunLoopTimeout(void *arg){struct __timeout_context *context = (struct __timeout_context *)arg;context->termTSR = 0ULL;CFRUNLOOP_WAKEUP_FOR_TIMEOUT();CFRunLoopWakeUp(context->rl);// The interval is DISPATCH_TIME_FOREVER, so this won't fire again}__CFRunLoopTimeout 函数内部1.将 「超时上下文」 结构体的 termTSR 属性赋值为02.CFRUNLOOP_WAKEUP_FOR_TIMEOUT() 宏内部是一个 do-while(0) 的无用操作,可以忽略3.然后通过 CFRunLoopWakeUp 函数来唤醒 runloop*/dispatch_source_set_event_handler_f(timeout_timer, __CFRunLoopTimeout);// 设置 GCD 对象的取消回调 可以理解为取消任务时的回调dispatch_source_set_cancel_handler_f(timeout_timer, __CFRunLoopTimeoutCancel);// 将开始时间加上给定的 runloop 过期时间,因为此时结果为 秒,需要转换为 纳秒,所以再乘以 10 ^ 9uint64_t ns_at = (uint64_t)((__CFTSRToTimeInterval(startTSR) + seconds) * 1000000000ULL);// 设置 GCD 定时器// 第二个参数表示定时器开始时间 也就是说,如果是 CFRunLoopRun 进来的话// 因为传入的值是 1.0e10,所以这个定时器永远不会触发,也就意味着 runloop 永远不会超时// 第三个参数表示 定时器 纳秒级的 间隔,这里传入的是 DISPATCH_TIME_FOREVER,说明定时器只会触发一次,不会触发多次// 第四个参数表示 定时器 纳秒级 的容错时间dispatch_source_set_timer(timeout_timer, dispatch_time(1, ns_at), DISPATCH_TIME_FOREVER, 1000ULL);// 开启 GCD 定时器dispatch_resume(timeout_timer);}else{ // infinite timeout// 设置超时时间为 9999999999// 然后设置 自定义的 「超时上下文」 结构体的 termTSR 属性值为 UNIT64_MAXseconds = 9999999999.0;timeout_context->termTSR = UINT64_MAX;}// 初始化一个布尔值 上一次是否通过 dispatch portBoolean didDispatchPortLastTime = true;int32_t retVal = 0;do{
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINIvoucher_mach_msg_state_t voucherState = VOUCHER_MACH_MSG_STATE_UNCHANGED;voucher_t voucherCopy = NULL;
#endif// 初始化一个 无符号 int8 整型类型的 消息缓冲区,缓冲区大小为 3KBuint8_t msg_buffer[3 * 1024];
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI// 初始化一个空的 mach_msg_header_t 类型的 msg 结构体指针变量mach_msg_header_t *msg = NULL;// 初始化一个值为 MACH_PORT_NULL 的 portmach_port_t livePort = MACH_PORT_NULL;// 将 mode 的 _portSet 取出__CFPortSet waitSet = rlm->_portSet;// 设置 runloop 底层数据结构的 ignoreWakeUps 值为 0__CFRunLoopUnsetIgnoreWakeUps(rl);// 判断如果 mode 的 observer 的 mask 与上 kCFRunLoopBeforeTimers 枚举值是否为1// 如果成功,通知 Observers 即将处理 Timersif (rlm->_observerMask & kCFRunLoopBeforeTimers)__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);// 判断如果 mode 的 observer 的 mask 与上 kCFRunLoopBeforeSources 枚举值是否为1// 如果成功,通知 Observers 即将处理 Sourcesif (rlm->_observerMask & kCFRunLoopBeforeSources)__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);// 处理 Blocks,传入 runloop 和 mode__CFRunLoopDoBlocks(rl, rlm);// 处理 Source0,传入 runloop 和 mode,以及 stopAfterHandleBoolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);// 如果处理 Sources0 成功,再次处理 Blocksif (sourceHandledThisLoop){__CFRunLoopDoBlocks(rl, rlm);}// 如果本次 loop 执行 source0 成功 或 超时时间为0,设置 poll 为 trueBoolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR);// 如果主队列 port 不为空且上一次 loop 没有主队列 portif (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime){
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI// 初始化一个 mach_msg_header_t 结构的 msgmsg = (mach_msg_header_t *)msg_buffer;// 判断主队列的port上是否有消息,如果有,进入 handle_msg 流程if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)){// 说明主队列的port上有消息过来,需要唤醒 runloopgoto handle_msg;}
#endif}// 将上一次 loop 主队列 值为空didDispatchPortLastTime = false;// 说明没有从 port 拉取消息,并且 mode 的 _observerMask 与上 kCFRunLoopBeforeWaiting 为真,// 则通知 Observers 即将休眠if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting))__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);/**CF_INLINE void __CFRunLoopSetSleeping(CFRunLoopRef rl){__CFBitfieldSetValue(((CFRuntimeBase *)rl)->_cfinfo[CF_INFO_BITS], 1, 1, 1);}*/// 设置 runloop sleeping 的flag__CFRunLoopSetSleeping(rl);// do not do any user callouts after this point (after notifying of sleeping)// 在 runloop 已经睡眠之后,不要做任何用户调用// 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./**//必须在每个循环中都将 local-to-this-activation 端口推入//迭代,因为此模式可以重新运行,我们不//希望这些端口可以被服务。*/// 将主队列 port 加入 waitSet 中__CFPortSetInsert(dispatchPort, waitSet);// 解锁 mode__CFRunLoopModeUnlock(rlm);// 解锁 runloop__CFRunLoopUnlock(rl);// 如果 poll 为真,sleepStart 为 0,否则为当前时间CFAbsoluteTime sleepStart = poll ? 0.0 : CFAbsoluteTimeGetCurrent();#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
#if USE_DISPATCH_SOURCE_FOR_TIMERS// 这个 do - while 循环主要针对于 GCD 定时器do{if (kCFUseCollectableAllocator){// objc_clear_stack(0);// <rdar://problem/16393959>// 将 msg_buffer 缓冲区 置为0memset(msg_buffer, 0, sizeof(msg_buffer));}msg = (mach_msg_header_t *)msg_buffer;// 判断 waitSet 的 port 是否有消息__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);// 判断 modeQueuePort 这个 port 不为空,且 livePort 为 modeQueuePortif (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./**清空内部队列。 如果其中一个标注块设置了 timerFired 标志,请中断并为计时器提供服务。*/while (_dispatch_runloop_root_queue_perform_4CF(rlm->_queue));// 如果 mode 的 _timerFired 为 真,则置为 false,并退出 do-while 循环if (rlm->_timerFired){// Leave livePort as the queue port, and service timers belowrlm->_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);
#elseif (kCFUseCollectableAllocator){// objc_clear_stack(0);// <rdar://problem/16393959>// 将 msg_buffer 缓冲区 置为0memset(msg_buffer, 0, sizeof(msg_buffer));}msg = (mach_msg_header_t *)msg_buffer;// 判断 waitSet 的 port 是否有消息__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
#endif#endif// 对 runloop 加锁__CFRunLoopLock(rl);// 对 mode 加锁__CFRunLoopModeLock(rlm);// 如果 poll 为真,说明主队了唤醒了 runloop,反之则获得 runloop 的睡眠时间rl->_sleepTime += (poll ? 0.0 : (CFAbsoluteTimeGetCurrent() - sleepStart));// 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./**CF_INLINE kern_return_t __CFPortSetRemove(__CFPort port, __CFPortSet portSet){if (MACH_PORT_NULL == port){return -1;}return mach_port_extract_member(mach_task_self(), port, portSet);}*/// 将 dispatchPort 从 waitSet 中移除,因为每次 runloop 迭代中 dispatchPort 都会被加入 waitSet__CFPortSetRemove(dispatchPort, waitSet);/**CF_INLINE void __CFRunLoopSetIgnoreWakeUps(CFRunLoopRef rl){rl->_perRunData->ignoreWakeUps = 0x57414B45; // 'WAKE'}唤醒 runloop*/__CFRunLoopSetIgnoreWakeUps(rl);// user callouts now OK again// 设置 runloop 的 sleeping flag 为 0__CFRunLoopUnsetSleeping(rl);// 通知 Observers 结束休眠if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting))__CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);handle_msg:;/**CF_INLINE void __CFRunLoopSetIgnoreWakeUps(CFRunLoopRef rl){rl->_perRunData->ignoreWakeUps = 0x57414B45; // 'WAKE'}表示收到了 port 消息,需要处理*/__CFRunLoopSetIgnoreWakeUps(rl);// 如果唤醒的端口为空,啥事也不干if (MACH_PORT_NULL == livePort){CFRUNLOOP_WAKEUP_FOR_NOTHING();// handle nothing}// 如果唤醒端口是 runloop 的 _wakeUpPort,则执行 CFRUNLOOP_WAKEUP_FOR_WAKEUP() 函数// CFRUNLOOP_WAKEUP_FOR_WAKEUP() 函数其实啥也没干else if (livePort == rl->_wakeUpPort){CFRUNLOOP_WAKEUP_FOR_WAKEUP();// do nothing on Mac OS}
#if USE_DISPATCH_SOURCE_FOR_TIMERS// 如果唤醒端口是 modeQueuePort,说明是 定时器唤醒了 runloopelse if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort){// 调用 CFRUNLOOP_WAKEUP_FOR_TIMER 函数,CFRUNLOOP_WAKEUP_FOR_TIMER();// 执行 timerif (!__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// 如果唤醒端口是 mode 的 _timerPortelse 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// 执行 timerif (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())){// Re-arm the next timer__CFArmNextTimerInMode(rlm, rl);}}
#endif// 如果唤醒端口是 dispatchPort,说明是主队列唤醒了 Runloopelse if (livePort == dispatchPort){// 执行 CFRUNLOOP_WAKEUP_FOR_DISPATCH() 函数CFRUNLOOP_WAKEUP_FOR_DISPATCH();// 解锁 mode__CFRunLoopModeUnlock(rlm);// 解锁 runloop__CFRunLoopUnlock(rl);// 设置 TSD,以 __CFTSDKeyIsInGCDMainQ 为 key, 6 为值_CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)6, NULL);// 执行主队列任务__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);// 设置 TSD,以 __CFTSDKeyIsInGCDMainQ 为 key, 0 为值_CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)0, NULL);// 对 runloop 加锁__CFRunLoopLock(rl);// 对 mode 加锁__CFRunLoopModeLock(rlm);// 标记本次 loop 处理了主队列sourceHandledThisLoop = true;didDispatchPortLastTime = true;}else{// 来到这,说明是被 source1 唤醒CFRUNLOOP_WAKEUP_FOR_SOURCE();// If we received a voucher from this mach_msg, then put a copy of the new voucher into TSD. CFMachPortBoost will look in the TSD for the voucher. By using the value in the TSD we tie the CFMachPortBoost to this received mach_msg explicitly without a chance for anything in between the two pieces of code to set the voucher again.voucher_t previousVoucher = _CFSetTSD(__CFTSDKeyMachMessageHasVoucher, (void *)voucherCopy, os_release);// Despite the name, this works for windows handles as well// 从端口中取出 source1CFRunLoopSourceRef rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort);if (rls){
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINImach_msg_header_t *reply = NULL;// 执行 source1 z任务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);}}// Restore the previous voucher_CFSetTSD(__CFTSDKeyMachMessageHasVoucher, previousVoucher, os_release);}
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINIif (msg && msg != (mach_msg_header_t *)msg_buffer)free(msg);
#endif// 处理 Blocks,传入 runloop 和 mode__CFRunLoopDoBlocks(rl, rlm);/**CFRunLoopRunInMode() 函数返回的原因,有四种// Reasons for CFRunLoopRunInMode() to Returnenum {kCFRunLoopRunFinished = 1, // 完成了 loopkCFRunLoopRunStopped = 2,  // loop 被终止kCFRunLoopRunTimedOut = 3, // loop 超时kCFRunLoopRunHandledSource = 4 // loop 执行了 主队列任务};*/// 如果执行了主队列任务并且 stopAfterHandle 为真,则退出 do-while 循环if (sourceHandledThisLoop && stopAfterHandle){retVal = kCFRunLoopRunHandledSource;}// 如果超时,则退出 do-while 循环else if (timeout_context->termTSR < mach_absolute_time()){retVal = kCFRunLoopRunTimedOut;}// 如果 runloop 已经停止,则退出 do-while 循环else if (__CFRunLoopIsStopped(rl)){__CFRunLoopUnsetStopped(rl);retVal = kCFRunLoopRunStopped;}// 如果 mode 的 _stopped 属性为真,则退出 do-while 循环else if (rlm->_stopped){rlm->_stopped = false;retVal = kCFRunLoopRunStopped;}// 如果 mode 此时为空,则退出 do-while 循环else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)){retVal = kCFRunLoopRunFinished;}#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINIvoucher_mach_msg_revert(voucherState);os_release(voucherCopy);
#endif} while (0 == retVal);// 如果开启了 GCD 定时器if (timeout_timer){// 停掉定时器并释放资源dispatch_source_cancel(timeout_timer);dispatch_release(timeout_timer);}else{// 释放 超时上下文 结构体指针free(timeout_context);}return retVal;
}

__CFRunLoopRun 精简代码

static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode)
{int32_t retVal = 0;do{// 通知 Observers 即将处理 Timers__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);// 通知 Observers 即将处理 Sources__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);// 处理 Blocks__CFRunLoopDoBlocks(rl, rlm);// 处理 Source0if (__CFRunLoopDoSources0(rl, rlm, stopAfterHandle)){// 处理 Blocks__CFRunLoopDoBlocks(rl, rlm);}Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR);// 判断有无 Source1if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)){// 如果有 Source1,就跳转到 handle_msggoto handle_msg;}didDispatchPortLastTime = false;// 通知 Observers 即将休眠__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);__CFRunLoopSetSleeping(rl);CFAbsoluteTime sleepStart = poll ? 0.0 : CFAbsoluteTimeGetCurrent();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);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 belowrlm->_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);// 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{// 被 Source1 唤醒__CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop;}// 处理 Blocks__CFRunLoopDoBlocks(rl, rlm);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)){retVal = kCFRunLoopRunFinished;}voucher_mach_msg_revert(voucherState);os_release(voucherCopy);} while (0 == retVal);return retVal;
}

RunLoop 执行的任务

通过 __CFRunLoopRun,我们知道了 RunLoop 执行的任务有以下几个:

  • __CFRunLoopDoObservers
  • __CFRunLoopDoBlocks
  • __CFRunLoopDoSources0
  • __CFRunLoopDoSource1
  • __CFRunLoopDoTimers
  • __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__

下面,我们将一个一个进行分析:

__CFRunLoopDoObservers

/* rl is locked, rlm is locked on entrance and exit */
static void __CFRunLoopDoObservers() __attribute__((noinline));
static void __CFRunLoopDoObservers(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopActivity activity)
{ /* DOES CALLOUT */CHECK_FOR_FORK();// 判断传入的 mode 对应的 observer 数量是否为 0CFIndex cnt = rlm->_observers ? CFArrayGetCount(rlm->_observers) : 0;if (cnt < 1)return;/* Fire the observers */// #define STACK_BUFFER_DECL(T, N, C) T N[C]// 这里可以分为两种情况// 如果 mode 中的 observer 数量小于等于 1024 个,那么相当于 CFRunLoopObserverRef buffer[cnt]// 如果 mode 中的 observer 数量大于 1024 个,那么相当于 CFRunLoopObserverRef buffer[1]// 所以这里通过 observer 数量来分配不同大小的缓存区STACK_BUFFER_DECL(CFRunLoopObserverRef, buffer, (cnt <= 1024) ? cnt : 1);// 判断如果 observer 数量小于 1024 个,则把上一步得到的 buffer 缓存区赋值给 collectedObservers// 如果 observer 数量大于 1024 个,则调用 malloc 函数来分配空间, 最终空间大小为 =  observer数目 * CFRunLoopObserverRef 类型占用内存大小// 最终获得了一个 类型为 CFRunLoopObserverRef 的数组指针变量 collectedObserversCFRunLoopObserverRef *collectedObservers = (cnt <= 1024) ? buffer : (CFRunLoopObserverRef *)malloc(cnt * sizeof(CFRunLoopObserverRef));CFIndex obs_cnt = 0;// 循环遍历传入的 runloop mode 中所有的 observersfor (CFIndex idx = 0; idx < cnt; idx++){// 从 observers 集合中取出一个 observerCFRunLoopObserverRef rlo = (CFRunLoopObserverRef)CFArrayGetValueAtIndex(rlm->_observers, idx);// 满足下列三个条件后进入 if 内部的逻辑// 1.判断 observer 当前的状态是否与传入的状态匹配,这里是通过 与 运算来完成,如果相等,结果应为 1,所以这里判断是不等于 0// 2.判断 observer 是否是有效的/**// Bit 3 in the base reserved bits is used for invalid state in run loop objectsCF_INLINE Boolean __CFIsValid(const void *cf){return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)cf)->_cfinfo[CF_INFO_BITS], 3, 3);}CF_INLINE void __CFSetValid(void *cf){__CFBitfieldSetValue(((CFRuntimeBase *)cf)->_cfinfo[CF_INFO_BITS], 3, 3, 1);}CF_INLINE void __CFUnsetValid(void *cf){__CFBitfieldSetValue(((CFRuntimeBase *)cf)->_cfinfo[CF_INFO_BITS], 3, 3, 0);}*/// 3.判断 observer 是否已经 fire 了/**Bit 0 of the base reserved bits is used for firing stateBit 1 of the base reserved bits is used for repeats stateCF_INLINE Boolean __CFRunLoopObserverIsFiring(CFRunLoopObserverRef rlo){return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)rlo)->_cfinfo[CF_INFO_BITS], 0, 0);}*//**探索第二步和第三步操作之后,可以发现 CFRuntimeBase 结构中的Bit 0 是用来表示 启动状态Bit 1 是用来表示 重复状态Bit 3 是用来表示 runloop 对象的可用状态*/if (0 != (rlo->_activities & activity) && __CFIsValid(rlo) && !__CFRunLoopObserverIsFiring(rlo)){// 对 observer 进行 retain,然后存入 collectedObservers 集合中// CoreFoundation 框架中 需要手动MRC,也就是对接收到的对象需要调用 CFRetain,与此同时,需要有配对的 CFRelease 操作防止内存泄漏collectedObservers[obs_cnt++] = (CFRunLoopObserverRef)CFRetain(rlo);}}// 对传入的 mode 解锁// 底层实现是一个互斥锁/**CF_INLINE void __CFRunLoopModeUnlock(CFRunLoopModeRef rlm){//CFLog(6, CFSTR("__CFRunLoopModeLock unlocking %p"), rlm);pthread_mutex_unlock(&(rlm->_lock));}*/__CFRunLoopModeUnlock(rlm);// 对传入的 runloop 解锁// 底层实现是一个互斥锁/**CF_INLINE void __CFRunLoopUnlock(CFRunLoopRef rl){//    CFLog(6, CFSTR("__CFRunLoopLock unlocking %p"), rl);pthread_mutex_unlock(&(((CFRunLoopRef)rl)->_lock));}*/__CFRunLoopUnlock(rl);// 根据上面 for 循环内部自增的变量 obs_cnt ,也就是有效的 observer 的个数进行遍历for (CFIndex idx = 0; idx < obs_cnt; idx++){// 取出一个 observerCFRunLoopObserverRef rlo = collectedObservers[idx];// 对 observer 进行加锁// 底层实现是一把互斥锁/**CF_INLINE void __CFRunLoopObserverLock(CFRunLoopObserverRef rlo){pthread_mutex_lock(&(rlo->_lock));//    CFLog(6, CFSTR("__CFRunLoopObserverLock locked %p"), rlo);}*/__CFRunLoopObserverLock(rlo);// 再次判断 observer 是否已失效if (__CFIsValid(rlo)){// 从 observer 中取出 重复状态,然后取反,赋值给 doInvalidate 布尔变量// 这个布尔变量在后面用作判断,即如果 observer 的 repeat 为真,那么 doInvalidate 就为假,// 如果 observer 的 repeat 为假,那么 doInvalidate 就为真,// 就会调用 CFRunLoopObserverInvalidate 来将 observer 从所属 runloop 中剔除,然后 observer 被释放Boolean doInvalidate = !__CFRunLoopObserverRepeats(rlo);// 设置 observer 的 runtimebase 里面的 bit 0 值为1 ,即标记 observer 已经启动/**CF_INLINE void __CFRunLoopObserverSetFiring(CFRunLoopObserverRef rlo){__CFBitfieldSetValue(((CFRuntimeBase *)rlo)->_cfinfo[CF_INFO_BITS], 0, 0, 1);}*/__CFRunLoopObserverSetFiring(rlo);// 对 observer 解锁__CFRunLoopObserverUnlock(rlo);// 调用 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ 函数,传入 observer 对应的 callout,以及 observer 自己,runloop 当前状态,以及 observer 上下文信息__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(rlo->_callout, rlo, activity, rlo->_context.info);if (doInvalidate){CFRunLoopObserverInvalidate(rlo);}// 取消 observer 的 firing 状态__CFRunLoopObserverUnsetFiring(rlo);}else{// 说明 observer 已经失效,对 observer 解锁__CFRunLoopObserverUnlock(rlo);}// 因为此时 observer 要么已经触发了回调,要么由于已经失效啥也没干,所以需要释放释放掉 observer,然后开启下一次循环过程CFRelease(rlo);}// 对 runloop 加锁__CFRunLoopLock(rl);// 对 mode 加锁__CFRunLoopModeLock(rlm);// 判断集合如果不等于 buffer ,说明是使用的 malloc 函数初始化而来,需要调用 free 函数释放if (collectedObservers != buffer)free(collectedObservers);
}

可以看到最终执行回调任务的是 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__,我们来到它的内部:

static void __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__() __attribute__((noinline));
static void __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(CFRunLoopObserverCallBack func, CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
{if (func){func(observer, activity, info);}asm __volatile__(""); // thwart tail-call optimization
}

__CFRunLoopDoBlocks

/**从 runloop 的 _block_item 链表里面匹配 mode,匹配上了的就执行 block 任务*/
static Boolean __CFRunLoopDoBlocks(CFRunLoopRef rl, CFRunLoopModeRef rlm)
{ // Call with rl and rlm locked/*因为 runloop 内部有这样的结构struct _block_item *_blocks_head;  // 链表的头指针struct _block_item *_blocks_tail;  // 链表的尾指针struct _block_item{struct _block_item *_next;     // 指向下一个 _block_itemCFTypeRef _mode; // CFString or CFSet // mode 可能是字符串,也可能是集合void (^_block)(void);          // block};所以 runloop 底层是以单链表的方式存储着 _block_item 结构体指针对象*/// 如果 block 链表头指针为空,说明当前 runloop 没有要执行的 block,直接返回 falseif (!rl->_blocks_head)return false;// 如果 mode 为空或者 mode 的名称为空,也直接返回 falseif (!rlm || !rlm->_name)return false;// 是否执行了 blockBoolean did = false;// 取出 block 链表头指针struct _block_item *head = rl->_blocks_head;// 取出 block 链表尾指针struct _block_item *tail = rl->_blocks_tail;// 将 runloop 上的 block 链表头指针置空rl->_blocks_head = NULL;// 将 runloop 上的 block 链表尾指针置空rl->_blocks_tail = NULL;// 获取 runloop 的 commonModesCFSetRef commonModes = rl->_commonModes;// 获取传入的 mode 的名称CFStringRef curMode = rlm->_name;// 对 mode 解锁__CFRunLoopModeUnlock(rlm);// 对 runloop 解锁__CFRunLoopUnlock(rl);// 初始化一个空的指针struct _block_item *prev = NULL;// 初始化一个指向链表头结点的指针struct _block_item *item = head;// 从取得的 block 链表头指针开始遍历整个 block 链表while (item){// 当前拿到的 _block_item 结构体变量struct _block_item *curr = item;// 指针往前移动一个位置,用于下一次循环item = item->_next;// 初始化 doit 布尔值Boolean doit = false;// 判断 _block_item 的 mode 是字符串还是集合,即这个 block_item 是对应一个 mode 还是多个 modeif (CFStringGetTypeID() == CFGetTypeID(curr->_mode)){// 说明是一个 mode// 判断两种情况// 1.传入的 mode 是否与 block_item 内部的 mode 相等// 2.判断 block_item 内部的 mode 是否为 kCFRunLoopCommonModes,同时判断 commonModes 里面是否有传入的 mode// 以上情况有一个满足,则 doit 为 true,否则 doit 为 falsedoit = CFEqual(curr->_mode, curMode) || (CFEqual(curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(commonModes, curMode));}else{// 说明是多个 mode// 也是判断两种情况// 1.判断 _block_item 内部的 mode 集合是否包含传入的 mode// 2.判断 _block_item 内部的 mode 集合是否包含 kCFRunLoopCommonModes,以及 commonModes 里面是否有传入的 mode// 以上情况有一个满足,则 doit 为 true,否则 doit 为 falsedoit = CFSetContainsValue((CFSetRef)curr->_mode, curMode) || (CFSetContainsValue((CFSetRef)curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(commonModes, curMode));}// 说白了只要 _block_item 节点满足 mode 匹配关系,就要执行其内部的 block 任务// 如果 doit 为 false,说明要执行 block 任务的不是当前的 _block_item,则将 prev 指针指向当前的 _block_item// 然后因为在前面已经做过 item = item->_next 的操作了,此时的 item 已经指向下一个 _block_item 了if (!doit)prev = curr;// 如果 doit 为 true,说明要执行 block 任务的就是当前的 _block_itemif (doit){// 这里其实可以分为三种情况// 一.目标节点在头尾之间的任一节点// 二.目标节点在头节点// 三.目标节点在尾节点// 因为已经执行完了 block 任务,需要删除目标节点// 判断 prev 指针是否为空,如果不为空,则将 prev 指针的 _next 指向 item,注意,此时 item 为下一次要遍历的 _block_item// 这里的作用就是情况一if (prev)prev->_next = item;// 下面分别判断了两种边界值情况,即要执行 block 的任务为头节点或尾节点// 判断如果当前正在遍历的 _block_time 是否等于 head 指针// 因为 head 在进入 while 循环之前指向的链表的头结点,所以能够进入 if 内部逻辑的条件// 说白了也就是刚好头结点就是要执行 block 任务的那个节点,此时将 head 指针指向下一节点,在循环结束后有一个判断,// 如果 head 不为空,然后里面的有一步操作就是重新让 rl->_blocks_head 指向 head,也就是说 执行了 block 任务且刚好为头节点的节点被删除了。// 这里的作用就是情况二if (curr == head)head = item;// 判断如果当前正在遍历的 _block_time 是否等于 tail 指针// 因为 tail 指针在 while 循环之前是指向的链表的尾节点,所有能够进入 if 内部逻辑条件// 说白了就是刚好尾节点就是要执行 block 任务的那个节点,此时将 tail 指针指向 prev 指针指向的节点,// 而 prev 指针的指向,我们知道,显然就是尾节点的前一个节点,说白了就是把执行了 block 任务且刚好为尾节点的节点被删除了// 这里的作用就是情况三if (curr == tail)tail = prev;// 取出如果当前正在遍历的 _block_item 中的 blockvoid (^block)(void) = curr->_block;// 释放如果当前正在遍历的 _block_item 的 modeCFRelease(curr->_mode);// 释放如果当前正在遍历的 _block_itemfree(curr);// 再次判断 doitif (doit){// 传入 block,执行任务__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block);// 将 did 置为 truedid = true;}// 释放 blockBlock_release(block); // do this before relocking to prevent deadlocks where some yahoo wants to run the run loop reentrantly from their dealloc// 在重新加锁之前释放 blcok 可以防止程序员在 dealloc 方法里面重新让 runloop 运行起来而导致死锁的情况}}// 对 runloop 加锁__CFRunLoopLock(rl);// 对  mode 加锁__CFRunLoopModeLock(rlm);// 如果 head 指针不为空,这里 if 的逻辑也是必进if (head){// 因为在前面已经对 rl->_blocks_head 指针进行了置空操作,这里等价于 tail->_next = NULLtail->_next = rl->_blocks_head;// 让 rl->_blocks_head 指向 head 指针指向的节点// 如果在上面的 while 循环中,没找到要执行的 block,那么 head 其实就是原来 rl->_blocks_head 的值// 如果在上面的 while 循环中,找到了要执行的 block,那么 head 是指向的rl->_blocks_head = head;// 因为在前面已经对 rl->_blocks_tail 指针进行了置空操作,所以这里 if 的逻辑必进// 所以这里就是让 rl->_blocks_tail 指向 tail 指针指向的节点if (!rl->_blocks_tail)rl->_blocks_tail = tail;}return did;
}

可以看到真正执行回调函数的是 CFRUNLOOP_IS_CALLING_OUT_TO_A__BLOCK 函数,其内部实现如下

static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__() __attribute__((noinline));
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(void (^block)(void))
{if (block){block();}asm __volatile__(""); // thwart tail-call optimization
}

__CFRunLoopDoSources0

/* rl is locked, rlm is locked on entrance and exit */
static Boolean __CFRunLoopDoSources0(CFRunLoopRef rl, CFRunLoopModeRef rlm, Boolean stopAfterHandle) __attribute__((noinline));
static Boolean __CFRunLoopDoSources0(CFRunLoopRef rl, CFRunLoopModeRef rlm, Boolean stopAfterHandle)
{ /* DOES CALLOUT */CHECK_FOR_FORK();// 初始化 sources ,并置空CFTypeRef sources = NULL;// 初始化返回值 sourceHandled,并置为 false ,意为是否已经处理了 source0Boolean sourceHandled = false;/* Fire the version 0 sources */if (NULL != rlm->_sources0 && 0 < CFSetGetCount(rlm->_sources0)){// 判断 mode 的 _source0 不为空且大小大于0// CFSetApplyFunction (Calls a function once for each value in the set.)// 这个函数的作用是对传入的 set 里面的每个元素执行 传入的函数指针// 第一个参数是 set// 第二个参数是 要对set每个元素执行一次的函数指针// 第三个参数作为传入的函数指针的第二个参数CFSetApplyFunction(rlm->_sources0, (__CFRunLoopCollectSources0), &sources);}// 此时 sources 里面已经有全部的 source0 了if (NULL != sources){// 对 mode 解锁__CFRunLoopModeUnlock(rlm);// 对 runloop 解锁__CFRunLoopUnlock(rl);// sources 有可能是一个 runloop source 也有可能是一个装的 runloop source 的数组// sources is either a single (retained) CFRunLoopSourceRef or an array of (retained) CFRunLoopSourceRefif (CFGetTypeID(sources) == CFRunLoopSourceGetTypeID()){// sources 是单个 runloop source// 类型强转一下CFRunLoopSourceRef rls = (CFRunLoopSourceRef)sources;// 对 rls 加锁__CFRunLoopSourceLock(rls);if (__CFRunLoopSourceIsSignaled(rls)){// 来到这,说明 rls 被标记为 signaled// 取消 signaled 的标志__CFRunLoopSourceUnsetSignaled(rls);// 判断 rls 是否有效if (__CFIsValid(rls)){// 对 rls 进行解锁__CFRunLoopSourceUnlock(rls);// 执行 source0 回调/**static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__() __attribute__((noinline));static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(void (*perform)(void *), void *info){if (perform){perform(info);}asm __volatile__(""); // thwart tail-call optimization}*/__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(rls->_context.version0.perform, rls->_context.version0.info);CHECK_FOR_FORK();// 将处理 source0 的结果置为 truesourceHandled = true;}else{__CFRunLoopSourceUnlock(rls);}}else{// 对 rls 解锁,来到这,说明 rls 没有被标记为 signaled__CFRunLoopSourceUnlock(rls);}}else{// sources 是数组// 取出 source0 的个数CFIndex cnt = CFArrayGetCount((CFArrayRef)sources);// 对 sources 进行排序,按照 _order 进行升序排序/**static CFComparisonResult __CFRunLoopSourceComparator(const void *val1, const void *val2, void *context){CFRunLoopSourceRef o1 = (CFRunLoopSourceRef)val1;CFRunLoopSourceRef o2 = (CFRunLoopSourceRef)val2;if (o1->_order < o2->_order)return kCFCompareLessThan;if (o2->_order < o1->_order)return kCFCompareGreaterThan;return kCFCompareEqualTo;}*/CFArraySortValues((CFMutableArrayRef)sources, CFRangeMake(0, cnt), (__CFRunLoopSourceComparator), NULL);// 遍历 sources 数组for (CFIndex idx = 0; idx < cnt; idx++){// 取出一个 source0 rlsCFRunLoopSourceRef rls = (CFRunLoopSourceRef)CFArrayGetValueAtIndex((CFArrayRef)sources, idx);// 对 rls 加锁__CFRunLoopSourceLock(rls);if (__CFRunLoopSourceIsSignaled(rls)){// 来到这,说明 rls 被标记为 signaled// 取消 signaled 的标志__CFRunLoopSourceUnsetSignaled(rls);// 判断 rls 是否有效if (__CFIsValid(rls)){// 对 rls 进行解锁__CFRunLoopSourceUnlock(rls);// 执行 source0 回调/**static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__() __attribute__((noinline));static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(void (*perform)(void *), void *info){if (perform){perform(info);}asm __volatile__(""); // thwart tail-call optimization}*/__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(rls->_context.version0.perform, rls->_context.version0.info);CHECK_FOR_FORK();// 将处理 source0 的结果置为 truesourceHandled = true;}else{__CFRunLoopSourceUnlock(rls);}}else{// 对 rls 解锁,来到这,说明 rls 没有被标记为 signaled__CFRunLoopSourceUnlock(rls);}// 如果有一个 source0 处理完成且 传入的 stopAfterHandle 为 true,则跳出循环if (stopAfterHandle && sourceHandled){break;}}}// 释放 sourcesCFRelease(sources);// 对 runloop 加锁__CFRunLoopLock(rl);// 对 mode 解锁__CFRunLoopModeLock(rlm);}// 返回处理 source0 结果return sourceHandled;
}

而真正执行回调的是 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ 函数,其内部实现如下

static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__() __attribute__((noinline));
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(void (*perform)(void *), void *info)
{if (perform){perform(info);}asm __volatile__(""); // thwart tail-call optimization
}

__CFRunLoopDoSource1

// msg, size and reply are unused on Windows
static Boolean __CFRunLoopDoSource1() __attribute__((noinline));
static Boolean __CFRunLoopDoSource1(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopSourceRef rls
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI,mach_msg_header_t *msg, CFIndex size, mach_msg_header_t **reply
#endif
)
{ /* DOES CALLOUT */CHECK_FOR_FORK();// 初始化 是否处理成功 source1 结果Boolean sourceHandled = false;/* Fire a version 1 source */// 对 source1 进行 retianCFRetain(rls);// 对 mode 解锁__CFRunLoopModeUnlock(rlm);// 对 runloop 解锁__CFRunLoopUnlock(rl);// 对 source1 加锁__CFRunLoopSourceLock(rls);// 如果 source1 有效if (__CFIsValid(rls)){/**// Bit 1 of the base reserved bits is used for signalled stateCF_INLINE Boolean __CFRunLoopSourceIsSignaled(CFRunLoopSourceRef rls){return (Boolean)__CFBitfieldGetValue(rls->_bits, 1, 1);}CF_INLINE void __CFRunLoopSourceSetSignaled(CFRunLoopSourceRef rls){__CFBitfieldSetValue(rls->_bits, 1, 1, 1);}CF_INLINE void __CFRunLoopSourceUnsetSignaled(CFRunLoopSourceRef rls){__CFBitfieldSetValue(rls->_bits, 1, 1, 0);}*/// 设置 source1 状态为未激活__CFRunLoopSourceUnsetSignaled(rls);// 解锁 source1__CFRunLoopSourceUnlock(rls);__CFRunLoopDebugInfoForRunLoopSource(rls);// 执行 source1 回调__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__(rls->_context.version1.perform,
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINImsg, size, reply,
#endifrls->_context.version1.info);CHECK_FOR_FORK();sourceHandled = true;}else{// 写入到日志,source1 失效if (_LogCFRunLoop){CFLog(kCFLogLevelDebug, CFSTR("%p (%s) __CFRunLoopDoSource1 rls %p is invalid"), CFRunLoopGetCurrent(), *_CFGetProgname(), rls);}// 对 source1 解锁__CFRunLoopSourceUnlock(rls);}// 释放 source1CFRelease(rls);// 对 runloop 加锁__CFRunLoopLock(rl);// 对 mode 加锁__CFRunLoopModeLock(rlm);return sourceHandled;
}

其中真正的回调函数为 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ ,其内部实现为:


static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__() __attribute__((noinline));
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__(
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINIvoid *(*perform)(void *msg, CFIndex size, CFAllocatorRef allocator, void *info),mach_msg_header_t *msg, CFIndex size, mach_msg_header_t **reply,
#elsevoid (*perform)(void *),
#endifvoid *info)
{if (perform){
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI*reply = perform(msg, size, kCFAllocatorSystemDefault, info);
#elseperform(info);
#endif}asm __volatile__(""); // thwart tail-call optimization
}

__CFRunLoopDoTimers

// rl and rlm are locked on entry and exit
static Boolean __CFRunLoopDoTimers(CFRunLoopRef rl, CFRunLoopModeRef rlm, uint64_t limitTSR)
{ /* DOES CALLOUT */// 初始化 timer 是否被处理成功 结果Boolean timerHandled = false;// 初始化一个空的 timers 集合CFMutableArrayRef timers = NULL;// 遍历 mode 的 _itemrs 定时器数组for (CFIndex idx = 0, cnt = rlm->_timers ? CFArrayGetCount(rlm->_timers) : 0; idx < cnt; idx++){// 取出 timerCFRunLoopTimerRef rlt = (CFRunLoopTimerRef)CFArrayGetValueAtIndex(rlm->_timers, idx);// 如果 timer 有效且尚未被触发if (__CFIsValid(rlt) && !__CFRunLoopTimerIsFiring(rlt)){// 并且 timer 的触发时间小于等于 限定的时间if (rlt->_fireTSR <= limitTSR){// 初始化 timers 集合if (!timers)timers = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);// 把 timer 加入 timers 集合CFArrayAppendValue(timers, rlt);}}}// 遍历 timers 集合,里面装的都是要干活的 timerfor (CFIndex idx = 0, cnt = timers ? CFArrayGetCount(timers) : 0; idx < cnt; idx++){// 取出 timerCFRunLoopTimerRef rlt = (CFRunLoopTimerRef)CFArrayGetValueAtIndex(timers, idx);// 执行 timer 回调Boolean did = __CFRunLoopDoTimer(rl, rlm, rlt);timerHandled = timerHandled || did;}// 释放 timers 集合if (timers)CFRelease(timers);return timerHandled;
}static Boolean __CFRunLoopDoTimer(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopTimerRef rlt)
{ /* DOES CALLOUT */// 初始化 timer 被处理成功结果Boolean timerHandled = false;uint64_t oldFireTSR = 0;/* Fire a timer */// 对 timer retainCFRetain(rlt);// 对 timer 加锁__CFRunLoopTimerLock(rlt);// 如果 timer 有效,并且 timer 触发时间点未到,以及 timer 没有被触发,以及 timer 的 runloop 就是传入的 runloop ,进入 if 逻辑if (__CFIsValid(rlt) && rlt->_fireTSR <= mach_absolute_time() && !__CFRunLoopTimerIsFiring(rlt) && rlt->_runLoop == rl){// 初始化空的 context_infovoid *context_info = NULL;// 初始化空的 context_release 函数指针void (*context_release)(const void *) = NULL;// 如果 timer 的 context 属性的 retain 函数指针不为空,进入 if 逻辑if (rlt->_context.retain){// 将 info retain 后赋值给 context_infocontext_info = (void *)rlt->_context.retain(rlt->_context.info);// 取出 timer 的 release 函数指针赋值给 context_releasecontext_release = rlt->_context.release;}else{// 取出 timer 的 infocontext_info = rlt->_context.info;}// timer 的时间间隔是否为 0Boolean doInvalidate = (0.0 == rlt->_interval);// 设置 timer 的 是否触发的 bits 为1__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;// 解锁 timer__CFRunLoopTimerUnlock(rlt);// 对 timer 触发加锁__CFRunLoopTimerFireTSRLock();// 取出 _fireTSRoldFireTSR = rlt->_fireTSR;// 对 timer 触发解锁__CFRunLoopTimerFireTSRUnlock();// 装配下一个 timer__CFArmNextTimerInMode(rlm, rl);// 对 mode 解锁__CFRunLoopModeUnlock(rlm);// 对 runloop 解锁__CFRunLoopUnlock(rl);// 执行 timer 真正的回调__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(rlt->_callout, rlt, context_info);CHECK_FOR_FORK();// 如果 timer 只跑一次if (doInvalidate){// 让 timer 失效CFRunLoopTimerInvalidate(rlt); /* DOES CALLOUT */}// 如果 context_release 函数指针不为空,则释放掉 context_infoif (context_release){context_release(context_info);}// 对 runloop 加锁__CFRunLoopLock(rl);// 对 mode 解锁__CFRunLoopModeLock(rlm);// 对 timer 加锁__CFRunLoopTimerLock(rlt);// 设置 处理 timer 结果为 truetimerHandled = true;// 设置 timer 的 是否触发的 bits 为0__CFRunLoopTimerUnsetFiring(rlt);}// 如果 timer 有效,并且 timer 被处理成功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 hereCRSetCrashLogMessage("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{// 解锁 timer__CFRunLoopTimerUnlock(rlt);}// 释放掉 timerCFRelease(rlt);return timerHandled;
}

其中真正执行回调的是 CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION 函数,其内部实现如下:

static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__() __attribute__((noinline));
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(CFRunLoopTimerCallBack func, CFRunLoopTimerRef timer, void *info)
{if (func){func(timer, info);}asm __volatile__(""); // thwart tail-call optimization
}

CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE

static void __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__() __attribute__((noinline));
static void __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(void *msg)
{_dispatch_main_queue_callback_4CF(msg);asm __volatile__(""); // thwart tail-call optimization
}

总结

参考资料

深入理解 RunLoop - ibireme

解密 RunLoop - MrPeak

Core Foundation 详解

iOS 查漏补缺 - RunLoop相关推荐

  1. iOS 查漏补缺 - PerformSelector

    performSelector 系列的函数我们都不陌生,但是对于它不同的变种以及底层原理在很多时候还是容易分不清楚,所以笔者希望通过 runtime 源码以及 GUNStep 源码来一个个抽丝剥茧,把 ...

  2. iOS 查漏补缺 - LLVM Clang

    LLVM 是一个自由软件项目,它是一种编译器基础设施,以 C++ 写成,包含一系列模块化的编译器组件和工具链,用来开发编译器前端和后端.它是为了任意一种编程语言而写成的程序,利用虚拟技术创造出编译时期 ...

  3. 104道 CSS 面试题,助你查漏补缺(下)

    作者:CavsZhouyou https://github.com/CavsZhouyou/Front-End-Interview-Notebook/blob/master/Css/Css.md 本部 ...

  4. 【面试题】104道 CSS 面试题,助你查漏补缺(下)

    作者:CavsZhouyou https://github.com/CavsZhouyou/Front-End-Interview-Notebook/blob/master/Css/Css.md 本部 ...

  5. css 图片自适应_104道 CSS 面试题,助你查漏补缺(下)

    (给前端大全加星标,提升前端技能) 作者:CavsZhouyou https://github.com/CavsZhouyou/Front-End-Interview-Notebook/blob/ma ...

  6. 算法岗面经整理!查漏补缺

    ↑↑↑关注后"星标"Datawhale 每日干货 & 每月组队学习,不错过 Datawhale干货 作者:阿毛冲冲冲,来源:NewBeeNLP(牛客网) 写在前面 三月面试 ...

  7. 前端面试查漏补缺--(一) 防抖和节流

    前言 本系列最开始是为了自己面试准备的.后来发现整理越来越多,差不多有十二万字符,最后决定还是分享出来给大家. 为了分享整理出来,花费了自己大量的时间,起码是只自己用的三倍时间.如果喜欢的话,欢迎收藏 ...

  8. 计算机三级网络技术查漏补缺

    计算机三级网络技术查漏补缺 DHCP(Dynamic Host Configuration Protocol) DMZ(demilitarized zone) 可信计算机评估准则 VLAN 集线器工作 ...

  9. 2019/5/12 查漏补缺

    目录 2019/5/12 查漏补缺 数据类型分为两大类:基本类型和引用类型: java中类的继承关系 关于接口 重载和重写 静态变量 java中的关键字和保留字 数据库操作 实现数据库收回部分权限的操 ...

最新文章

  1. AI人才「用工荒」如何解决?看看这几家顶级公司的应对策略
  2. 迷你世界支持服务器,迷你世界云服务器
  3. 理解 ActivityExecutionContextManager
  4. 各种光源(灯)的光谱
  5. linux安装mysql不成功怎么处理_Linux上安装MySQL时出现不兼容的解决办法
  6. HTML页面代码移动端和pc兼容,pc端网站如何实现移动端适配?
  7. 疑难杂症--由于系统缓冲区空间不足或队列已满,不能执行套接字上的操作
  8. 如何绘制逻辑图 — 3.要素的属性:粒度与分层
  9. android webview调js方法,Android中WebView与H5的交互,Native与JS方法互调
  10. VisualStudio2022如何改为中文语言(vs2022汉化)
  11. 贪吃蛇-单机游戏-微信小程序项目开发流程详解
  12. 推荐一个app,收纳杭州最全登山地图!附亲测过的亲子徒步路线推荐
  13. 计算机一级仿宋gb2312,Word怎么设置仿宋体显示为仿宋GB2312字体?
  14. boost vs loki 分类的第一篇文章,我的boost,loki体验
  15. 自学习策略和Levy飞行的正弦余弦优化算法-附代码
  16. 华为路由器:虚拟路由冗余协议VRRP的讲解
  17. 手机摄影你不能不知的 5 个拍照小技巧,原来这拍摄模式那么强大
  18. 组合学:使用10个数字与52个字母生成1477万个不重复的4位串码V4衍生版本
  19. 水平集LevelSet 分割图像
  20. 前置++和后置++的区别

热门文章

  1. 服务器驱动用什么工具_驱动、改向滚筒用什么胶板进行包胶?
  2. 源码学习笔记-网易邮箱登陆页面
  3. 第3章:表、栈和队列
  4. 中科院 陈玉福算法简答题
  5. 记一次Android Studio的Enable VT-x in your BIOS问题解决
  6. oracle10g企业管理器打不开,2014年05月13日  Oracle 10g的企业管理器(Enterprise Manager)的常见问题...
  7. Mac连接远程服务器命令
  8. N年前的笔记-智能ABC输入法
  9. 【8088】基于8088最小系统板的打地鼠游戏实现【简】
  10. 在EXCEL表格中如何进行排序(如按照成绩总分排名)