IOS底层 - Runloop底层总结
前言
一、什么是Runloop,以及它的作用
二、NSRunloop和CFRunLoopRef的区别
三、Runloop的底层结构
四、分析Runloop和线程的关系
五、Runloop的Mode
六、Timer的添加流程分析
七、Runloop的工作流程
八、Source0和Source1
九、 Observer
在多线程的总结中,提到了不少有关于Runloop的地方,今天就来具体的总结一下Runloop的相关知识点。
一、什么是Runloop,以及它的作用
Runloop简单的来说就是运行循环,内部是一个do-while循环。它保证了程序的持续运行。是一种保障线程循环处理事件而不会退出的机制。
它主要有以下3个作用:
(1)保持程序的持续运行。
(2)处理App中各种事件(触摸、手势、定时器)。
(3)节省cpu的资源、提高程序的性能,该休息的时候休息、该工作的时候工作。
二、NSRunloop和CFRunLoopRef的区别
首先NSRunLoop是基于CoreFoundation中CFRunLoopRef的一层封装,这个IOS开发者都知道。
重点是NSRunLoop是非线程安全的,而CFRunLoopRef是线程安全的。
为什么NSRunLoop不是线程安全呢?
线程安全是指:一块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源。当多线程去读取同一块资源的时候是线程安全的,但是当去修改同一块资源的时候就可能会出现数据错乱的情况了。
CFRunLoopRef提供的API中,操作Runloop的函数都加了一把互斥锁。
CF_INLINE void __CFRunLoopLock(CFRunLoopRef rl) {
pthread_mutex_lock(&(((CFRunLoopRef)rl)->_lock));
所以它是线程安全的。
而NSRunLoop官方文档明确写道,它是非线程安全的:

另外通过GNUStep的源码去查看它的方法内部实现都没有加锁,所以它是非线程安全的。
三、Runloop的底层结构
首先分析一下Runloop的结构,通过CFRunLoop的源码来具体分析。
typedef struct __CFRunLoop * CFRunLoopRef;
__CFRunLoop是c的结构体,其中重要的成员如下:
struct __CFRunLoop {
.......
pthread_mutex_t _lock; /* locked for accessing mode list */
pthread_t _pthread; //线程
uint32_t _winthread; //windows平台使用
CFMutableSetRef _commonModes; //UITrackingRunLoopMode/kCFRunLoopDefaultMode
CFMutableSetRef _commonModeItems; //observer/source/timer
CFRunLoopModeRef _currentMode;
CFMutableSetRef _modes; //内置的modes
.............
};
核心成员:
_lock:用来保证访问mode list的线程安全
_pthread:每一个RunLoop都对应一条线程
_commonModes:将需要同时处理事件的多个Mode(包括UITrackingRunLoopMode和kCFRunLoopDefaultMode等)放入_commonModes集合进行标记。
_commonModeItems:对应observer/source/timer
_currentMode:当前的模式
_modes:内置的模式
重点:_commonModeItems 与 _commonModes是kCFRunLoopCommonModes (NSRunLoopCommonModes)实现逻辑。比如当我们添加一个timer的时候,指定的模式为NSRunLoopCommonModes,此时timer会被添加到_commonModeItems中,并将timer追加到_commonModes中已经标记的模式下。
四、分析Runloop和线程的关系
Runloop的核心成员中有一个_pthread,也就是每一个Runloop都会有唯一对应的线程。
那么线程和Runloop是如何建立联系的呢?
1、通过CFRunLoop的Api函数:CFRunLoopGetCurrent(获取当前的Runloop)和CFRunLoopGetMain(获取主线程的Runloop)来分析:
CFRunLoopRef CFRunLoopGetMain(void) {
//进程检查
CHECK_FOR_FORK();
//保存在常量区
static CFRunLoopRef __main = NULL; // no retain needed
//判断__main是否存在,如果不存在执行_CFRunLoopGet0
if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np()); // no CAS needed(不需要原子性)
return __main;
}
CFRunLoopRef CFRunLoopGetCurrent(void) {
//进程检查
CHECK_FOR_FORK();
//通过线程的私有数据获取对应的Runloop
CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
//如果有就返回RunLoop
if (rl) return rl;
//如果没有执行_CFRunLoopGet0
return _CFRunLoopGet0(pthread_self());
}
在分析_CFRunLoopGet0之前,先看下TSD是什么。
2、TSD (Thread-specific data)相关: 源码
Thread-specific data是线程私有数据,RunLoop会保存在线程的私有数据里面。
typedef struct __CFTSDTable {
//记录destructors数组元素的个数
uint32_t destructorCount;
//数组用来保存私有数据
uintptr_t data[CF_TSD_MAX_SLOTS];
//用来保存释放函数
tsdDestructor destructors[CF_TSD_MAX_SLOTS];
} __CFTSDTable;
// For the use of CF and Foundation only
//是获取__CFTSDTable的data数据,并返回slot的值
CF_EXPORT void *_CFGetTSD(uint32_t slot) {
if (slot > CF_TSD_MAX_SLOTS) {
_CFLogSimple(kCFLogLevelError, "Error: TSD slot %d out of range (get)", slot);
HALT;
}
__CFTSDTable *table = __CFTSDGetTable();
if (!table) {
_CFLogSimple(kCFLogLevelWarning, "Warning: TSD slot %d retrieved but the thread data has already been torn down.", slot);
return NULL;
}
uintptr_t *slots = (uintptr_t *)(table->data);
return (void *)slots[slot];
}
// For the use of CF and Foundation only
//给__CFTSDTable里设置data[slot]和destructors[slot]位置的值。
CF_EXPORT void *_CFSetTSD(uint32_t slot, void *newVal, tsdDestructor destructor) {
if (slot > CF_TSD_MAX_SLOTS) {
_CFLogSimple(kCFLogLevelError, "Error: TSD slot %d out of range (set)", slot);
HALT;
}
__CFTSDTable *table = __CFTSDGetTable();
if (!table) {
_CFLogSimple(kCFLogLevelWarning, "Warning: TSD slot %d set but the thread data has already been torn down.", slot);
return NULL;
}
void *oldVal = (void *)table->data[slot];
table->data[slot] = (uintptr_t)newVal;
table->destructors[slot] = destructor;
return oldVal;
}
3、_CFRunLoopGet0分析
//全局字典
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
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
//判断传入的线程是否为空
if (pthread_equal(t, kNilPthreadT)) {
//如果为空默认主线程
t = pthread_main_thread_np();
}
__CFLock(&loopsLock);
//判断全局字典是否为空
if (!__CFRunLoops) {
__CFUnlock(&loopsLock);
//如果为空,创建临时字典
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
//创建runloop
CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
//以线程地址为key,runloop为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) {
//如果loop没找到,那么创建一个
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
__CFLock(&loopsLock);
//再获取一次
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
if (!loop) {
//如果loop仍然不存在,将新创建的newLoop存入全局字典中
CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
loop = newLoop;
}
// 不要在 loopsLock 中释放运行循环,因为 CFRunLoopDeallocate 可能最终会占用它
// don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it
__CFUnlock(&loopsLock);
CFRelease(newLoop);
}
//判断t是否是当前线程,如果是当前线程,将runloop放入线程私有数据中
if (pthread_equal(t, pthread_self())) {
// `CFInternal.h`定了枚举`__CFTSDKeyRunLoop` = 10 与 `__CFTSDKeyRunLoopCntr` = 11
// 如果线程TSD,枚举`__CFTSDKeyRunLoopCntr` 对应`slot`未存值
_CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL);
if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) {
//为其存值`PTHREAD_DESTRUCTOR_ITERATIONS-1`,并设置析构函数`__CFFinalizeRunLoop`,
//__CFFinalizeRunLoop 释放函数 指针,为了当线程销毁的来调用退出runloop
_CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop);
}
}
return loop;
}
//线程销毁时,会销毁线程对应的TSD
static void __CFTSDFinalize(void *arg) {
//获取私有数据表
__CFTSDTable *table = (__CFTSDTable *)arg;
//遍历所有slot 比如存RunLoop的__CFTSDKeyRunLoop以及__CFTSDKeyRunLoopCntr
for (int32_t i = 0; i < CF_TSD_MAX_SLOTS; i++) {
if (table->data[i] && table->destructors[i]) {
uintptr_t old = table->data[i];
table->data[i] = (uintptr_t)NULL;
//当遍历到__CFTSDKeyRunLoopCntr时,执行释放runloop的释放函数
table->destructors[i]((void *)(old));
}
}
if (table->destructorCount == PTHREAD_DESTRUCTOR_ITERATIONS - 1) {
free(table);
__CFTSDSetSpecific(CF_TSD_BAD_PTR);
return;
}
}
主要流程是:
(1)判断传入的线程是否为空,如果为空默认设置为主线程
(2)判断全局字典是否为空,如果为空,创建主线程的Runloop,并添加到临时字典中(以线程地址为key,runloop为value),然后将这个临时字典复制到全局字典中。
(3)从全局字典获取对应线程的RunLoop,如果未找到,就创建一个。
(4)判断传入的线程是否是当前线程,如果是则把当前的RunLoop和释放函数指针保存到线程的私有数据中。
(5)最后返回runloop。
4、RunLoop的创建和销毁
创建Runloop:
static CFRunLoopRef __CFRunLoopCreate(pthread_t t) {
CFRunLoopRef loop = NULL;
CFRunLoopModeRef rlm;
//计算runloop的空间
uint32_t size = sizeof(struct __CFRunLoop) - sizeof(CFRuntimeBase);
//通过_CFRuntimeCreateInstance创建runloop
loop = (CFRunLoopRef)_CFRuntimeCreateInstance(kCFAllocatorSystemDefault, CFRunLoopGetTypeID(), size, NULL);
if (NULL == loop) {
return NULL;
}
//设置loop的状态数据
(void)__CFRunLoopPushPerRunData(loop);
__CFRunLoopLockInit(&loop->_lock);
loop->_wakeUpPort = __CFPortAllocate();
if (CFPORT_NULL == loop->_wakeUpPort) HALT;
__CFRunLoopSetIgnoreWakeUps(loop);
//设置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
//查找mode,如果没有就创建
rlm = __CFRunLoopFindMode(loop, kCFRunLoopDefaultMode, true);
if (NULL != rlm) __CFRunLoopModeUnlock(rlm);
return loop;
}
其创建过程总结如下:
(1)通过_CFRuntimeCreateInstance创建一个RunLoop对象
(2)设置Runloop的属性,以及通过调用__CFRunLoopFindMode来创建模式并添加到modes集合中。
释放Runloop:
CF_PRIVATE void __CFFinalizeRunLoop(uintptr_t data) {
CFRunLoopRef rl = NULL;
if (data <= 1) {
__CFLock(&loopsLock);
//移除全局字典中线程对应的runloop
if (__CFRunLoops) {
rl = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(pthread_self()));
if (rl) CFRetain(rl);
CFDictionaryRemoveValue(__CFRunLoops, pthreadPointer(pthread_self()));
}
__CFUnlock(&loopsLock);
} else {
_CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(data - 1), (void (*)(void *))__CFFinalizeRunLoop);
}
if (rl && CFRunLoopGetMain() != rl) { // protect against cooperative threads
if (NULL != rl->_counterpart) {
CFRelease(rl->_counterpart);
rl->_counterpart = NULL;
}
// purge all sources before deallocation
//清除所有mode下的事件
CFArrayRef array = CFRunLoopCopyAllModes(rl);
for (CFIndex idx = CFArrayGetCount(array); idx--;) {
CFStringRef modeName = (CFStringRef)CFArrayGetValueAtIndex(array, idx);
__CFRunLoopRemoveAllSources(rl, modeName);
}
__CFRunLoopRemoveAllSources(rl, kCFRunLoopCommonModes);
CFRelease(array);
}
//释放runloop
if (rl) CFRelease(rl);
}
主要流程如下:
(1)线程退出时会调用__CFFinalizeRunLoop。
(2)移除全局字典中当前应线程的runloop。
(3)移除所有mode下的事件源。
(4)释放runloop
5、程序启动的时候会默认创建主线程的Runloop
了解了Runloop的创建和释放流程之后,来到main函数
int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
@autoreleasepool {
// Setup code that might create autoreleased objects goes here.
appDelegateClassName = NSStringFromClass([AppDelegate class]);
}
//第三个参数:NSString * _Nullable principalClassName 默认是UIApplication
// If nil is specified for principalClassName, the value for NSPrincipalClass from the Info.plist is used. If there is no
// NSPrincipalClass key specified, the UIApplication class is used. The delegate class will be instantiated using init.
// 如果 principalClassName 指定为 nil,则使用 Info.plist 中 NSPrincipalClass 的值。 // 如果没有指定 NSPrincipalClass 键,则使用 UIApplication 类。 委托类将使用 init 实例化。
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
main函数中,非常关键的方法是 " return UIApplicationMain(argc, argv, nil, appDelegateClassName)"
这里会return并执行UIApplicationMain方法。假如这里直接return返回了,那么程序就结束了。所以UIApplicationMain方法中必然会开启一个Runloop保持程序的运行。
我在AppDelegate入口下一个断点:

主线程的堆栈信息如下:

根据堆栈信息,可以确定UIApplicationMain方法中,开启了Runloop并运行。
我下一个符号断点“CFRunLoopGetMain”:

在UIApplicationInitialize方法中,首次去执行了CFRunLoopGetMain,创建了主线程的Runloop,并且将UITrackingRunLoopMode模式添加到CommonModes中。
总结:
(1)Runloop和线程是一一对应的关系
(2)程序在启动的时候,会默认开启主线程的Runloop。
(3)当线程销毁的时候,Runloop会自动销毁。
五、Runloop的Mode
1、关于RunLoop的mode,先来看一张图:

一个RunLoop中,包含多个Mode,通过上面分析的RunLoop的结构就可以知道。
一个Mode包含Source0、Source1、Timers、Observers(统称item)。
struct __CFRunLoopMode {
.........
CFStringRef _name;
...............
CFMutableSetRef _sources0;
CFMutableSetRef _sources1;
CFMutableArrayRef _observers;
CFMutableArrayRef _timers;
..................
};
__CFRunLoopMode同样是一个结构体。
每次运行RunLoop都需要指定一个Mode,该Mode会被设置为_currentMode,只有与该Mode关联的输入源source0、source1才能处理,同样监听runloop的运行,只有与该Mode关联的observers才能收到通知,以及与该Mode关联的Timer才会启动。
那么__CFRunLoopMode有几种类型呢?
常用的共有5种:
(1)KCFRunloopDefaultMode: 默认模式,主线程是在这个运行模式下运行的。
(2)UITrackingRunLoopMode: 跟踪用户交互事件
(3)UIInitializationRunLoopMode:在刚启动App时进入的一个Mode,启动完成就不在使用了。
(4)GSEventReceiveRunLoopMode:系统内部事件,通常用不到
(5)kCFRunLoopCommonModes: 伪模式,不是一种真正的运行模式,是同步Source/Timer/Observer到多个Mode中的一种解决方案。
2.自定义Mode
除了以上的5种模式,也可以自定义Mode。
但是自定义的模式中必须有关联的事件,否则自定义模式没有任何意义
......
@property (nonatomic, strong) NSTimer *timer;
........
static NSString * const kRunLoopCustomMode1 = @"CustomMode1";
static NSString * const kRunLoopCustomMode2 = @"CustomMode2";
.......
/// 自定义mode
- (void)func {
[NSThread detachNewThreadWithBlock:^{
self.timer = [NSTimer timerWithTimeInterval:1 repeats:true block:^(NSTimer * _Nonnull timer) {
NSLog(@"1");
}];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:kRunLoopCustomMode1];
[[NSRunLoop currentRunLoop] runMode:kRunLoopCustomMode1 beforeDate:[NSDate distantFuture]];
}];
}
- (void)dealloc {
[self.timer invalidate];
}
为了不防止主线程的运行,我们创建一个子线程,然后将timer加入到自定义的mode中并启动runloop在自定义的mode下运行。
注意:以上代码添加自定义mode没有问题,但是会造成循环引用。因为创建的子线程和子线程的runloop放在全局字典中,被全局字典所持有,然后线程又持有了self。self又持有了timer。就会形成:
thead(runloop) -> self -> timer(runloop)
当离开页面的时候,self需要等thread释放,thread需要等timer释放,runloop停止(runloop停止之后,线程执行完任务就会自动销毁,线程自动销毁之后,就会执行上面分析的__CFFinalizeRunLoop,移除全局字典中此线程对应的runloop),timer又要等self释放,也就形成了循环引用。所以为了打破循环引用,此时需要用__weak修饰当前self。
/// 自定义mode
- (void)func {
__weak typeof(self)weakSelf = self;
[NSThread detachNewThreadWithBlock:^{
weakSelf.timer = [NSTimer timerWithTimeInterval:1 repeats:true block:^(NSTimer * _Nonnull timer) {
NSLog(@"1");
}];
[[NSRunLoop currentRunLoop] addTimer:weakSelf.timer forMode:kRunLoopCustomMode1];
[[NSRunLoop currentRunLoop] runMode:kRunLoopCustomMode1 beforeDate:[NSDate distantFuture]];
}];
}
我们也可以把自定义的mode加入到commonModes中
- (void)func1 {
__weak typeof(self)weakSelf = self;
[NSThread detachNewThreadWithBlock:^{
weakSelf.timer = [NSTimer timerWithTimeInterval:1 repeats:true block:^(NSTimer * _Nonnull timer) {
NSLog(@"1");
}];
CFRunLoopAddCommonMode(CFRunLoopGetCurrent(), (__bridge CFStringRef)kRunLoopCustomMode1);
[[NSRunLoop currentRunLoop] addTimer:weakSelf.timer forMode:NSRunLoopCommonModes];
[[NSRunLoop currentRunLoop] runMode:kRunLoopCustomMode1 beforeDate:[NSDate distantFuture]];
}];
}
注意:如果要切换Runloop的模式,可以采用CFRunLoopStop()主动退出,只有runloop能退出,我们才又机会切换mode。
3. kCFRunLoopCommonModes
首先来一道IOS经典的面试:
主线程默认运行在KCFRunloopDefaultMode下,此时我们在主线程的RunLoop中添加一个timer,模式是KCFRunloopDefaultMode。当我们滑动tableView的时候,定时器就停止了,是为什么呢?
答案很简答:因为主线程的Runloop默认在KCFRunloopDefaultMode模式下,而Runloop每次循环都只会处理当前mode下的timer/source/observer,当我们滑动tableView的时候,Runloop的模式被切换为了UITrackingRunLoopMode,该模式下并没有添加当前的timer,而添加在Default模式下的timer不会运行。我们只需要在添加timer的时候把timer同时添加到UITrackingRunLoopMode下或者添加到kCFRunLoopCommonModes下即可。
六、Timer的添加流程分析
首先Timer的结构如下:
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 */
};
typedef struct CF_BRIDGED_MUTABLE_TYPE(NSTimer) __CFRunLoopTimer * CFRunLoopTimerRef;
显然NSTimer是对__CFRunLoopTimer的一层封装。
- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray<NSRunLoopMode> *)modes;
- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay;
而performSelector的延时API,底层也是基于Timer实现的。
所以当我们创建Timer,或者用performSelector延时函数的时候,最终都会添加到Runloop的mode下,才能运行。
用两个方法:(1)将timer分别添加到对应的mode (2)将timer添加到commonModes
- (void)func1 {
self.timer = [NSTimer timerWithTimeInterval:1 repeats:true block:^(NSTimer * _Nonnull timer) {}];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:UITrackingRunLoopMode];
}
- (void)func2 {
self.timer = [NSTimer timerWithTimeInterval:1 repeats:true block:^(NSTimer * _Nonnull timer) {}];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSLog(@"%@",[NSRunLoop currentRunLoop]);
}
通过打印先得出结论:
(1)当分别添加到对应的mode下时,timer会添加到每一个mode的timers数组中。
(2)当添加到commonModes中时,timer不仅会添加到每一个mode的timers数组中,并且会添加到common items中。
下面主要分析CFRunLoopAddTimer函数(去掉了非核心代码):
void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeName) {
//......
//判断如果runloop在释放,直接返回
if (__CFRunLoopIsDeallocating(rl)) return;
//.......
//判断当前传入的是否是kCFRunLoopCommonModes
if (modeName == kCFRunLoopCommonModes) {
//取出_commonModes中的mode
CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
//判断commonModeItems是否为空,如果为空就创建一个集合
if (NULL == rl->_commonModeItems) {
rl->_commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
}
//将timer添加到commonModeItems中
CFSetAddValue(rl->_commonModeItems, rlt);
if (NULL != set) {
//2个参数:1 runloop 2 timer
CFTypeRef context[2] = {rl, rlt};
/* add new item to all common-modes */
//set集合中每一个mode都调用__CFRunLoopAddItemToCommonModes方法
CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
CFRelease(set);
}
} else {
//通过__CFRunLoopFindMode获取mode
CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, true);
if (NULL != rlm) {
//判断timers并创建timers
if (NULL == rlm->_timers) {
CFArrayCallBacks cb = kCFTypeArrayCallBacks;
cb.equal = NULL;
rlm->_timers = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &cb);
}
}
//当mode有,但是定时器中modes又没有mode时
if (NULL != rlm && !CFSetContainsValue(rlt->_rlModes, rlm->_name)) {
.....
if (NULL == rlt->_runLoop) {
rlt->_runLoop = rl;
} else if (rl != rlt->_runLoop) {
//定时器已关联的runloop与当前runloop不一致,返回
//......
return;
}
//定时器的modes 添加该mode的名称
CFSetAddValue(rlt->_rlModes, rlm->_name);
//......
//采用mktimer(mach kernel timer)通过machport 和 machmsg 触发定时器事件 或者 gcd的timer
__CFRepositionTimerInMode(rlm, rlt, false);
//........
}
//......
}
//........
}
//将一个item添加到被commonModes标记的所有mode下
static void __CFRunLoopAddItemToCommonModes(const void *value, void *ctx) {
//模式
CFStringRef modeName = (CFStringRef)value;
//runloop
CFRunLoopRef rl = (CFRunLoopRef)(((CFTypeRef *)ctx)[0]);
//item
CFTypeRef item = (CFTypeRef)(((CFTypeRef *)ctx)[1]);
//判断类型并添加到指定的mode下
if (CFGetTypeID(item) == __kCFRunLoopSourceTypeID) {
CFRunLoopAddSource(rl, (CFRunLoopSourceRef)item, modeName);
} else if (CFGetTypeID(item) == __kCFRunLoopObserverTypeID) {
CFRunLoopAddObserver(rl, (CFRunLoopObserverRef)item, modeName);
} else if (CFGetTypeID(item) == __kCFRunLoopTimerTypeID) {
CFRunLoopAddTimer(rl, (CFRunLoopTimerRef)item, modeName);
}
}
以上就是添加timer的核心源码。
结论:
(1)当添加timer指定模式为非NSRunLoopCommonModes时,timer会被添加到指定mode下。
(2)当添加timer指定模式为NSRunLoopCommonModes时,timer会被添加到_commonModeItems中,并将timer追加到_commonModes中已经标记的模式下。
移除timer的函数就不分析了,基本原理差不多。
继续分析timer添加到_commonModeItems中用来做什么:
//将mode添加到commonModes中
void CFRunLoopAddCommonMode(CFRunLoopRef rl, CFStringRef modeName) {
CHECK_FOR_FORK();
if (__CFRunLoopIsDeallocating(rl)) return;
__CFRunLoopLock(rl);
if (!CFSetContainsValue(rl->_commonModes, modeName)) {
//获取_commonModeItems集合
CFSetRef set = rl->_commonModeItems ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModeItems) : NULL;
//将mode添加到_commonModes
CFSetAddValue(rl->_commonModes, modeName);
if (NULL != set) {
CFTypeRef context[2] = {rl, modeName};
/* add all common-modes items to new mode */
//将commonItems中的item都添加到新的mode下
CFSetApplyFunction(set, (__CFRunLoopAddItemsToCommonMode), (void *)context);
CFRelease(set);
}
} else {
}
__CFRunLoopUnlock(rl);
}
//将commonModeItems中的所有item添加到指定model下
static void __CFRunLoopAddItemsToCommonMode(const void *value, void *ctx) {
//item
CFTypeRef item = (CFTypeRef)value;
//runloop
CFRunLoopRef rl = (CFRunLoopRef)(((CFTypeRef *)ctx)[0]);
//mode
CFStringRef modeName = (CFStringRef)(((CFTypeRef *)ctx)[1]);
if (CFGetTypeID(item) == __kCFRunLoopSourceTypeID) {
CFRunLoopAddSource(rl, (CFRunLoopSourceRef)item, modeName);
} else if (CFGetTypeID(item) == __kCFRunLoopObserverTypeID) {
CFRunLoopAddObserver(rl, (CFRunLoopObserverRef)item, modeName);
} else if (CFGetTypeID(item) == __kCFRunLoopTimerTypeID) {
CFRunLoopAddTimer(rl, (CFRunLoopTimerRef)item, modeName);
}
}
例如:当我们指定一个timer为commonMode时,这个timer应该被追加到所有的mode下。如果一开始commonModes中就一个mode,timer也就是只能在这一个mode下运行。当我们再添加一个mode到commonModes中时,timer也需要被追加到新的mode下,此时commonItems就会发挥它的作用了,也就是当
调用CFRunLoopAddCommonMode时,会将commonItems中的所有item都追加到新的被commoModes标记的mode下。
七、Runloop的工作流程
经过以上分析,已经对Runloop的内部结构有了初步的认识,接下来就是Runloop的核心流程:

1、主流程
首先Runloop的入口函数:CFRunLoopRun 和 CFRunLoopRunInMode
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);
}
其中核心的函数是CFRunLoopRunSpecific(忽略非核心代码):
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */
//....
//根据modeName找到本次运行的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;
}
volatile _per_run_data *previousPerRun = __CFRunLoopPushPerRunData(rl);
//取上一次运行的mode
CFRunLoopModeRef previousMode = rl->_currentMode;
//如果本次mode和上次的mode一致
rl->_currentMode = currentMode;
//初始化一个result为kCFRunLoopRunFinished
int32_t result = kCFRunLoopRunFinished;
if (currentMode->_observerMask & kCFRunLoopEntry )
/// 1. 通知 Observers: RunLoop 即将进入 loop。
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
if (currentMode->_observerMask & kCFRunLoopExit )
/// 10. 通知 Observers: RunLoop 即将退出。
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
//.....
return result;
}
当Runloop进入循环后的核心函数为__CFRunLoopRun(忽略非核心代码):
补充: 由于最近看了swift开源的CoreFoundation库,对于USE_DISPATCH_SOURCE_FOR_TIMERS这个编译条件,更新如下:(ios系统下的NSTimer也使用了gcd的timer)
#if DEPLOYMENT_RUNTIME_OBJC
#define USE_DISPATCH_SOURCE_FOR_TIMERS __HAS_DISPATCH__
#else
#define USE_DISPATCH_SOURCE_FOR_TIMERS 0
#endif
/**
* 运行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();
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);
}
}
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;
} 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;
//开始进入do while循环
do {
//初始化一个存放内核消息的缓冲池
uint8_t msg_buffer[3 * 1024];
mach_msg_header_t *msg = NULL;
mach_port_t livePort = MACH_PORT_NULL;
//取所有需要监听的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) {
//从缓冲区读取消息
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;
}
}
didDispatchPortLastTime = false;
/// 6.通知 Observers: RunLoop 的线程即将进入休眠(sleep)。
if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
//设置RunLoop为休眠状态
__CFRunLoopSetSleeping(rl);
__CFPortSetInsert(dispatchPort, waitSet);
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
//这里有个内循环,用于接收等待端口的消息
//进入此循环后,线程进入休眠,直到收到新消息才跳出该循环,继续执行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端口的消息
/// • 一个基于 port 的Source 的事件。
/// • 一个 Timer 到时间了
/// • RunLoop 自身的超时时间到了
/// • 被其他什么调用者手动唤醒
__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);
__CFRunLoopLock(rl);
__CFRunLoopModeLock(rlm);
__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 (MACH_PORT_NULL == livePort) {
CFRUNLOOP_WAKEUP_FOR_NOTHING();
// handle nothing
} else if (livePort == rl->_wakeUpPort) {
CFRUNLOOP_WAKEUP_FOR_WAKEUP();
}
else if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
//dispatch_timer
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);
}
}
else if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) {
//mk_timer
CFRUNLOOP_WAKEUP_FOR_TIMER();
if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
// Re-arm the next timer
__CFArmNextTimerInMode(rlm, rl);
}
}
/// 9.2 如果有dispatch到main_queue的block,执行block
else if (livePort == dispatchPort) {
CFRUNLOOP_WAKEUP_FOR_DISPATCH();
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
_CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)6, NULL);
__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) {
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);
}
}
}
if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);
/// 执行加入到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;
}
上面的核心代码非常的长,总结大致流程如下:。
(1)判断RunLoop的状态是否关闭。
(2)判断runloop是否超时(利用gcd的timer)。
(3)初始化一个存放内核消息的缓冲池,取所有需要监听的port,设置RunLoop为可以被唤醒状态。
(4)通知 Observers: RunLoop 即将触发 Timer 回调。
(5)通知 Observers: RunLoop 即将触发 Source0 (非port) 回调。
(6) RunLoop 触发 Source0 (非port) 回调。
(7)如果有 Source1 (基于port) 处于 ready 状态,直接处理这个 Source1 然后跳转去处理消息。
(8)通知 Observers: RunLoop 的线程即将进入休眠(sleep)。设置RunLoop为休眠状态,之后就是用户态切换到内核态
(9)接收waitSet端口的消息
(10)取消runloop的休眠状态,通知 Observers: RunLoop 的线程刚刚被唤醒了。内核态切换到用户态不久
(11)收到消息,处理消息。(1、如果一个 Timer 到时间了,触发这个Timer的回调。2、如果有dispatch到main_queue的block,执行block 3、如果一个 Source1 (基于port) 发出事件了,处理这个事件)
(12)判断设置runloop的状态
流程图如下:

补充:
1.用户态:应用程序都是在用户态,平时开发用到的api等都是用户态的操作
2.内核态:系统调用,牵涉到操作系统,底层内核相关的指令。
当Runloop进入休眠的时候,其实就是用户态向内核态的转换,并不是一个white死循环,而是状态切换。
当Runloop被唤醒之后,就是内核态向用户态转换。
内核态和用户态的转换,实质是调用了mach_msg()函数。
2、RunLoop的状态总结
typedef CF_ENUM(SInt32, CFRunLoopRunResult) {
kCFRunLoopRunFinished = 1,
kCFRunLoopRunStopped = 2,
kCFRunLoopRunTimedOut = 3,
kCFRunLoopRunHandledSource = 4
};
(1)kCFRunLoopRunFinished 完成状态,当没有timer/source的时候
(2)kCFRunLoopRunStopped 停止状态,手动停止的时候
(3)kCFRunLoopRunTimedOut 超时状态,当Runloop运行超时的时候
(4)kCFRunLoopRunHandledSource 处理完事件就返回
3、RunLoop的活动状态总结
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), //进入RunLoop
kCFRunLoopBeforeTimers = (1UL << 1), //RunLoop即将触发定时器事件
kCFRunLoopBeforeSources = (1UL << 2), //RunLoop即将处理Source事件
kCFRunLoopBeforeWaiting = (1UL << 5), //RunLoop即将进入休眠
kCFRunLoopAfterWaiting = (1UL << 6), //RunLoop即将被唤醒,但尚未开始处理唤醒它的事件
kCFRunLoopExit = (1UL << 7), //退出RunLoop
kCFRunLoopAllActivities = 0x0FFFFFFFU
};
4、RunLoop的响应总结
(1)调用Timer (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);
}
getpid(); // thwart tail-call optimization
}
每当Timer的时间到了之后,会触发__CFRunLoopDoTimers -> __CFRunLoopDoTimer ->__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__的回调函数,通过保存在timer中的CFRunLoopTimerCallBack回调出来。
(2)GCD主队列(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);
getpid(); // thwart tail-call optimization
}
通过上面Runloop主流程分析,在Runloop唤醒之后,会判断是否存在GCD主队列的block,如果存在就会执行 CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE
当我使用主队列异步函数的时候,就会触发此回调。
(3)block应用 (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();
}
getpid(); // thwart tail-call optimization
}
当上层调用:
[[NSRunLoop currentRunLoop] performBlock:^{
NSLog(@"1");
}];
的时候会触发__CFRunLoopDoBlocks->CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK。
(4)响应Source0 (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);
}
getpid(); // thwart tail-call optimization
}
Runloop每一次循环都会去调用__CFRunLoopDoSources0->CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION。
(5)响应Source1 (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_MINI
void *(*perform)(void *msg, CFIndex size, CFAllocatorRef allocator, void *info),
mach_msg_header_t *msg, CFIndex size, mach_msg_header_t **reply,
#else
void (*perform)(void *),
#endif
void *info) {
if (perform) {
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
*reply = perform(msg, size, kCFAllocatorSystemDefault, info);
#else
perform(info);
#endif
}
getpid(); // thwart tail-call optimization
}
当Runloop收到内核事件之后,会调用__CFRunLoopDoSource1->CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION
(6)Observer源(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);
}
getpid(); // thwart tail-call optimization
}
当Runloop处于上面总结的活动状态时,会调用__CFRunLoopDoObservers->CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION
八、Source0和Source1
Source的结构如下:
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;
};
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);
void (*schedule)(void *info, CFRunLoopRef rl, CFRunLoopMode mode);
void (*cancel)(void *info, CFRunLoopRef rl, CFRunLoopMode mode);
void (*perform)(void *info);
} CFRunLoopSourceContext;
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_OSX || 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;
首先__CFRunLoopSource结构体里面,有一个联合体(为了节省内存开销)。联合体中也就是Source0和Source1。
1、Source0
先来看Source0,当我们在上层调用:
///子线程/主线程->主线程
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
///子线程/主线程->子线程,
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg
这些函数的时候,就会将事件添加到Source0中(子线程/主线程->子线程的方法必须保证执行事件的子线程Runlopp开启)。
模拟Souce0实现:
......
@property (nonatomic) CFRunLoopSourceRef source0;
@property (nonatomic) CFRunLoopRef runloop;
......
#pragma mark - TestSource0
- (void)performSelectorSource0 {
__weak typeof(self)weakSelf = self;
NSThread *thread = [[NSThread alloc] initWithBlock:^{
void *info = (__bridge void *)weakSelf;
CFRunLoopSourceContext context = {0,info,NULL,NULL,NULL,NULL,NULL,&schedule,&cancel,&perform};
weakSelf.source0 = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
CFRunLoopAddSource(CFRunLoopGetCurrent(),weakSelf.source0, kCFRunLoopDefaultMode);
weakSelf.runloop = CFRunLoopGetCurrent();
[[NSRunLoop currentRunLoop] run];
}];
thread.name = @"xxx";
[thread start];
}
///与Source0相关的回调
void schedule(void *info, CFRunLoopRef rl, CFRunLoopMode mode) {
NSLog(@"source0已加入子线程的runloop中");
}
void cancel(void *info, CFRunLoopRef rl, CFRunLoopMode mode) {
NSLog(@"source0已从子线程的runloop中移除");
}
void perform(void *info) {
NSLog(@"获取主线程的消息");
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//触发Source0
CFRunLoopSourceSignal(_source0);
//唤醒Runloop
CFRunLoopWakeUp(_runloop);
}
- (void)dealloc {
CFRunLoopRemoveSource(_runloop, _source0, kCFRunLoopDefaultMode);
CFRunLoopStop(_runloop);
}
Souce0相当于被包装的带有上下文的函数,需要主动触发,这个函数才会被执行。
2、Source1
Source1是基于mach_port的事件,它是内核事件,苹果系统的内核是XNU混合内核,包括了Mach内核和BSD内核。BSD主要提供在Mach之上标准化的API,Mach是核心。Mach负责线程与进程管理、虚拟内存管理、进程之间通信和消息传递、任务调度等。
基于Source1的事件传递,主要基于内核接口:
///System Trap / Function — Sends and receives a message using the same mes- sage buffer
mach_msg_return_t mach_msg(mach_msg_header_t *msg, mach_msg_option_t option, mach_msg_size_t send_size, mach_msg_size_t rcv_size, mach_port_name_t rcv_name, mach_msg_timeout_t timeout, mach_port_name_t notify
这个方法底层会基于硬件异常:陷阱(trap)实现事件传递。陷阱最终的用途,是在用户程序和内核之间提供接口,称为系统调用。
下面来模拟一下Source1事件
使用CFMessagePortRef来模拟Source1事件。
.....
@property (nonatomic) CFMessagePortRef subPort;
@property (nonatomic) CFMessagePortRef mainPort;
@property (nonatomic, strong) NSThread *subThread;
@end
.......
- (void)viewDidLoad {
[super viewDidLoad];
//创建主线程的port
[self createMainPort];
//创建子线程
__weak typeof(self)weakself = self;
[NSThread detachNewThreadWithBlock:^{
weakself.subThread = [NSThread currentThread];
[weakself createSubPort];
CFRunLoopRun();
}];
}
//创建主线程端口
- (void)createMainPort {
void *mainInfo = (__bridge void*)self;
CFMessagePortContext mainPortContext = {0,mainInfo,NULL,NULL,NULL};
Boolean shouldFreeInfo;
//创建mach_port
CFMessagePortRef mach_port = CFMessagePortCreateLocal(kCFAllocatorDefault, CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("com.test.main_mach_port")), &_messagePortCallBack, &mainPortContext, &shouldFreeInfo);
self.mainPort = mach_port;
if (self.mainPort != NULL) {
CFRunLoopSourceRef source1 = CFMessagePortCreateRunLoopSource(kCFAllocatorDefault, self.mainPort, 0);
if (source1 != NULL) {
CFRunLoopAddSource(CFRunLoopGetCurrent(), source1, kCFRunLoopDefaultMode);
CFRelease(source1);
CFRelease(mach_port);
}
}
}
//创建子线程端口
- (void)createSubPort {
void *info = (__bridge void*)self;
CFMessagePortContext portContext = {0,info,NULL,NULL,NULL};
Boolean shouldFreeInfo;
//创建mach_port
CFMessagePortRef mach_port = CFMessagePortCreateLocal(kCFAllocatorDefault, CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("com.test.sub_mach_port")), &_messagePortCallBack, &portContext, &shouldFreeInfo);
///保存端口,建立双向信道
self.subPort = mach_port;
if (self.subPort != NULL) {
CFRunLoopSourceRef source1 = CFMessagePortCreateRunLoopSource(kCFAllocatorDefault, self.subPort, 0);
if (source1 != NULL) {
CFRunLoopAddSource(CFRunLoopGetCurrent(), source1, kCFRunLoopDefaultMode);
CFRelease(source1);
CFRelease(mach_port);
}
}
}
//Source1的回调函数
CFDataRef _messagePortCallBack(CFMessagePortRef local, SInt32 msgid, CFDataRef data, void *info) {
///桥接info获取oc对象...
const UInt8 *buffer = CFDataGetBytePtr(data);
CFIndex index = CFDataGetLength(data);
CFStringRef messageref = CFStringCreateWithBytes(kCFAllocatorDefault, buffer, index, kCFStringEncodingUTF8, false);
NSString *message = (__bridge_transfer NSString*)messageref;
NSLog(@"%@:消息ID:%d 收到数据:%@", [NSThread currentThread],msgid,message);
return NULL;
}
//发送消息 主线程->主线程
- (void)mainToMain {
CFStringRef message = CFSTR("你好,我来自主线程");
CFDataRef outData = CFStringCreateExternalRepresentation(kCFAllocatorDefault, message, kCFStringEncodingUTF8, 0);
///发送消息, 10001 代表 主线程->主线程
CFMessagePortSendRequest(_mainPort, 1001, outData, 0.1, 0.0, NULL, NULL);
///释放资源
CFRelease(outData);
CFRelease(message);
}
//发送消息 主线程->子线程
- (void)mainToThread {
CFStringRef message = CFSTR("你好,我来自主线程");
CFDataRef outData = CFStringCreateExternalRepresentation(kCFAllocatorDefault, message, kCFStringEncodingUTF8, 0);
///发送消息, 10001 代表 主线程->子线程
CFMessagePortSendRequest(_subPort, 1002, outData, 0.1, 0.0, NULL, NULL);
///释放资源
CFRelease(outData);
CFRelease(message);
}
//发送消息 子线程->主线程
- (void)threadToMain {
[self performSelector:@selector(sendMsgToMainThread) onThread:self.subThread withObject:nil waitUntilDone:NO];
}
- (void)sendMsgToMainThread {
NSData *data = [@"你好,我来自子线程" dataUsingEncoding:NSUTF8StringEncoding];
CFDataRef msgData = (__bridge_retained CFDataRef)data;
CFMessagePortSendRequest(_mainPort, 1003, msgData, 0.1, 0.0, NULL, NULL);
CFRelease(msgData);
}
//发送消息 子线程->子线程
- (void)threadToThread {
[self performSelector:@selector(sendMsgToSubThread) onThread:self.subThread withObject:nil waitUntilDone:NO];
}
- (void)sendMsgToSubThread {
CFStringRef message = CFSTR("你好,我来自子线程");
CFDataRef outData = CFStringCreateExternalRepresentation(kCFAllocatorDefault, message, kCFStringEncodingUTF8, 0);
CFMessagePortSendRequest(_subPort, 1004, outData, 0.1, 0.0, NULL, NULL);
CFRelease(outData);
CFRelease(message);
}
以上就是基于CFMessagePortRef的demo。
当我们有子线程常驻需求的时候,我们就可以给子线程的RunLoop添加一个NSPort,然后run。Runloop就可以一直运行,线程就可以常驻了。
总结:
1、Source0就是一个包装了上下文信息的回调函数,需要主动通过CFRunLoopSourceSignal触发,然后通过CFRunLoopWakeUp唤醒Runloop去处理。它的主要作用是处理app内部事件以及app自己负责管理的事务。
2、Source1是基于mach_port实现的回调函数,可以实现多端通信。
九、 Observer
通过给Runloop添加Observer,可以来监听Runloop的事件状态。
Observer的结构如下:
struct __CFRunLoopObserver {
CFRuntimeBase _base;
_CFRecursiveMutex _lock;
CFRunLoopRef _runLoop;
CFIndex _rlCount; //被添加到Rl几次
CFOptionFlags _activities; //需要观察RL哪些状态
CFIndex _order; //状态事件触发时,依次通知的观察者,值越小优先级越高
CFRunLoopObserverCallBack _callout;//状态事件触发时的回调
CFRunLoopObserverContext _context; // 上下文
};
一个观察者,可以被添加到RunLoop的多个Mode下,同一个Mode下也可以有多个Oberserver,当事件触发时,观察者是依据_order值从小到大的顺序进行事件回调。
比如NSOrderedPerform的分类也是基于Observer的。
@interface NSRunLoop (NSOrderedPerform)
- (void)performSelector:(SEL)aSelector target:(id)target argument:(nullable id)arg order:(NSUInteger)order modes:(NSArray<NSRunLoopMode> *)modes;
- (void)cancelPerformSelector:(SEL)aSelector target:(id)target argument:(nullable id)arg;
- (void)cancelPerformSelectorsWithTarget:(id)target;
@end
自定义监听demo如下:
- (void)addRunloopObserver {
void *info = (__bridge void *)self;
CFRunLoopObserverContext context = {0,info,NULL,NULL,NULL};
_changeObserver = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, true, 0, _runLoopObserverCallBack, &context);
CFRunLoopAddObserver(CFRunLoopGetCurrent(), _changeObserver, kCFRunLoopCommonModes);
}
///回调函数
void _runLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {
///获取mode名称
NSString* mode = (__bridge NSString*)CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
if (info) {//桥接info为oc对象
NSLog(@"%@",info);
Test13ViewController *vc = (__bridge Test13ViewController*)info;
vc.view.backgroundColor = [UIColor whiteColor];
}
switch (activity) {
//do somthing...
case kCFRunLoopEntry:
NSLog(@"进入");
break;
case kCFRunLoopBeforeTimers:
NSLog(@"即将处理timer");
break;
case kCFRunLoopBeforeSources:
NSLog(@"即将处理Source0");
break;
case kCFRunLoopBeforeWaiting:
NSLog(@"即将休眠");
break;
case kCFRunLoopAfterWaiting:
NSLog(@"刚刚唤醒");
break;
case kCFRunLoopExit:
NSLog(@"退出");
break;
}
}
- (void)dealloc {
CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), _changeObserver, kCFRunLoopCommonModes);
CFRelease(_changeObserver);
NSLog(@"%s",__func__);
}