RunLoop是什么?Runloop还是比较顾名思义的,就是一种循环,只不过它的循环策略不同。一般的循环会导致CPU进入忙等待状态,而Runloop则是一种“闲”等待。当没有事件时,Runloop会进入睡眠状态,有事件发生时,RunLoop会去找对应的Handler处理事件。RunLoop可以让线程在需要做事的时候忙起来,不需要的话让线程休眠。
运行循环,在程序运行过程中循环做一些事情,如果没有RunLoop程序执行完毕就会立即退出,如果有RunLoop程序会一直运行,并且时时刻刻在等待用户的输入操作。RunLoop可以在需要的时候自己跑起来运行,在没有操作的时候就停下来休息。充分节省CPU资源,提高程序性能。
RunLoop基本作用
保持程序持续运行:程序已启动就会开一个主线程,主线程一开起来就会跑一个主线程对应的RunLoop,RunLoop保证主线程不会被销毁,也就保证了程序的持续运行。
处理App中的各种事件:触摸事件,定时器事件,Selector事件等
节省CPU资源,提高程序性能:程序运行起来时,当什么操作都没有做的时候,RunLoop就告诉CPU,现在没有事情做,我要去休息,这是CPU就会将其资源释放出来去做其他的事情,当有事情做的时候Runloop就会立马起来去做事情。

RunLoop在哪里开启
UIApplicationMain函数内启动了RunLoop,程序不会马上退出,而是保持运行状态。因此每一个应用必须要有一个RunLoop,我们知道主线程一开起来,就会跑一个和主线程对应的RunLoop,那么RunLoop一定是在程序的入口main函数中开启。
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
进入UIApplicationMain
UIKIT_EXTERN int UIApplicationMain(int argc, char *argc[], NSString * __nullable principalClassName, NSString * __nullable delegateClassName);
我们发现它返回的是int数,那么我们对main函数做一些修改
int main(int argc, char *argv[]) {
@autoreleasepool {
NSLog(@"开始");
int re = UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
NSLog(@"结束");
return re;
}
}
运行程序,我们发现只会打印开始,并不会打印结束,这说明在UIApplicationMain函数中,开启了一个和主线程相关的RunLoop,导致UIApplicationMain不会返回,一直在运行中,也就保证了程序的持续运行。
我们来看到RunLoop的源码:
// 用Default启动
void CFRunLoopRun(void) {
int32_t result;
do {
result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
CHECK_FOR_FORK();
}while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}
RunLoop确实是do while通过判断result的值实现的。因此,我们可以把RunLoop看成一个死循环。如果没有RunLoop,UIApplicationMain函数执行完毕之后将直接返回,也就是没有程序持续运行一说。

RunLoop对象
Foundation框架,基于CFRunLoopRef的封装
NSRunLoop对象
CoreFoundation
CFRunLoopRef对象
struct __CFRunLoop {
CFRuntimeBase _base;
pthread_mutex_t _lock; // locked for accessing mode list
__CFPort _wakeUpPort; // 内核向该端口发送消息可以环形RunLoop
Boolean _unused;
volatile _per_run_data *_perRunData; // reset for runs of the run loop
pthread_t _pthread; // RunLoop对应的线程
uint32_t _winthread;
CFMutableSetRef _commonModes; // 存储的是字符串,记录所有标记为common的mode
CFMutableSetRef _commonModeItems; // 存储所有commonMode的item(source、timer、observer)
CFRunLoopModeRef _currentMode; // 当前运行的mode
CFMutableSetRef _modes; // 存储的是CFRunLoopModeRef
struct _block_item *_blocks_head; // doblocks的时候用到
struce _block_item *_blocks_tail;
CFTypeRef _counterpart;
}
如何获取RunLoop对象
Foundation
[NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象
[NSRunLoop mainRunLoop]; // 获取主线程的RunLoop对象
Core Foundation
CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象
CFRunLoopGetMain(); // 获得主线程的RunLoop对象
RunLoop Mode
Mode可以视为事件的管家,一个Mode管理着各种事件,它的结构如下:
struct __CFRunLoopMode {
CFRuntimeBase _base;
pthread_mutex_t _lock; /*must have the run loop locked before locking this*/
CFStringRef _name; // mode名称
Boolean _stopped; // mode是否被终止
char _padding[3];
// 几种事件
CFMutableSetRef _sources0; // sources0
CFMutableSetRef _sources1; // sources1
CFMutableArrayRef _observers; // 通知
CFMutableArrayRef _timers; // 定时器
CFMutableDictionaryRef _portToV1SourceMap; // 字典 key是mach_port_t, value是CFRunLoopSourceRef
__CFPortSet _portSet; // 保存所有需要监听的port,比如_wakeUpPort, _timerPort都保存在这个数组中
CFIndex _observerMask;
#if USR_DISPATCH_SOURCE_FOR_TIMERS
dispatch_source_t _timerSource;
dispatch_queue_t _queue;
Boolean _timerFired;
Boolean _dispatchTimerArmed;
#endif
#if USE_MK_TIMER_TOO
mach_port_t _timerPort;
Boolean _mkTimerArmed;
#endif
#if DEPLOYMENT_TARGET_WINDOWS
DWORD _msgQMask;
void (*_msgPump)(void);
#endif
uint64_t _timerSoftDeadline;
uint64_t _timerHardDeadline;
}
一个CFRunLoopMode对象有一个name,若干个source0、source1、timer、observer和若干port,事件都是由Mode管理,RunLoop管理Mode。
从源码很容易看出,RunLoop总是运行在某种特定的CFRunLoopModeRef下(每次运行__CFRunLoopRun()函数时必须指定Mode)。而通过CFRunLoopRef对应结构体的定义可以很容易知道每种RunLoop都可以包含若干个Mode,每个Mode又包含Source、Timer、Observer。每次调用RunLoop的主函数__CFRunLoopRun()时必须指定一种Mode,这个Mode成为_currentMode,当切换Mode时必须退出当前mode,然后重新进入RunLoop以保证不同Mode的Source、Timer、Observer互不影响。
RunLoop Mode实际上是Source、Timer、Observer的集合,不同的Mode把不同组的Source、Timer、Observer隔绝开来。RunLoop在某个时刻只能跑在一个Mode下,处理这个Mode当中的Source、Timer和Observer。
- NSDefaultRunLoopMode
- NSConnectionReplyMode
- NSModalPanelRunLoopMode
- NSEventTrackingRunLoopMode
- NSRunLoopCommonModes
公开暴露出来的只有NSDefaultRunLoopMode和NSRunLoopCommonModes
NSRunLoopCommonModes实际是一个Mode集合,包含NSDefauleRunLoopMode和NSEventTrackingRunLoop,并不是Runloop会运行在kCFRunLoopCommModes这种模式下,而是相当于分别注册了NSDefaultRunLoopMode和UITrackingRunLoopMode。也可以通过调用CFRunLoopAddCommonMode()方法将自定义Mode放到kCFRunLoopCommonModes组合。
Mode | Name | Description |
---|---|---|
Default | NSDefaultRunLoopMode(Cocoa) kCFRunLoopDefaultMode(Core Foundation) | 用的最多的模式,大多数情况下应该使用该模式开始RunLoop并配置input source |
Connection | NSConnectionReplyMode(Cocoa) | Cocoa用这个模式组合NSConnection对象监测回应,我们应该很少使用这种模式 |
Modal | NSModalPanelRunLoopMode | Cocoa使用此模式来标识于模态面板的事件 |
Event tracking | NSEventTrackingRunLoopMode(Cocoa) | Cocoa使用此模式在鼠标拖动loop和其他用户界面跟踪loop期间限制传入事件 |
Common modes | NSRunLoopCommonModes(Cocoa) kCFRunLoopCommonModes(Core Foundation) | 这是一组可配置的常用模式。将输入源与这些模式相关联会与组中的每个模式相关联。Cocoa applications里面此集包含Default、Modal和Event tracking。Core Foundation只包括默认模式,你可以自己把自定义mode用CFRunLoopAddCommonMode函数加入到集合中。 |
RunLoop Source
RunLoop Source分别为Source、Observer、Timer三种,他们统称为ModeItem。
CFRunLoopSource
CFRunLoopSource分source0和source1两个版本,它的结构如下:
struct __CFRunLoopSource {
CFRuntimeBase _base;
uint32_t _bits; // 用于标记Signaled状态,source0只有在被标记为Signaled状态,才会被处理
pthread_mutex_t _lock;
CFIndex _order;
CFMutableBagRef _runLoops;
union {
CFRunLoopSourceContext version0;
CFRunLoopSourceContext1 version1;
} _context;
};
source0是App内部事件,由App自己管理的UIEvent、CFSocket都是source0.当一个source0事件准备执行的时候,必须要先把它标记为signal状态,以下是Source0的结构体:
typedef struct {
CFIndex version;
void *info;
const void *(*retain)(const void *info);
void (*release)(const void *info);
CFStringRef (*copyDescription)(const void *info);
Boolean (*equal)(const void *info1, const void *info2);
CFHashCode (*hash)(const void *info);
void (*schedule)(void *info, CFRunLoopRef rl, CFStringRef mode);
void (*cancel)(void *info, CFRunLoopRef rl, CFStringRef mode);
void (*perform)(void *info);
} CFRunLoopSourceContext;
source0是非基于Port的。只包含一个回调(函数指针),它并不能主动触发事件。使用时,需要先调用CFRunLoopSourceSignal(source),将这个Source标记为待处理,然后手动调用CFRunLoopWakeUp(runloop)来唤醒RunLoop,让其处理这个事件。
source1由RunLoop和内核管理,source1带有mach_port_t,可以接收内核消息并触发回调,以下是source1的结构体
typedef struct {
CFIndex version;
void *info;
const void *(*retain)(const void *info);
void (*release)(const void *info);
CFStringRef (*copyDescription)(const void *info);
Boolean (*equal)(const void *info1, const void *info2);
CFHashCode (*hash)(const void *info);
#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) || (TARGET_OS_EMBEDDED) || TARGET_OS_IPHONE)
mach_port_t (*getPort)(void *info);
void * (*perform)(void *msg, CFIndex size, CFAllocatorRef allocator, void *info);
#else
void * (*getPort)(void *info);
void (*perform)(void *info);
#endif
} CFRunLoopSourceContext1;
Source1除了包含回调指针外包含了一个mach port,Source1可以监听系统端口和通过内核和其他线程通信,接收、分发系统事件,它能够主动唤醒RunLoop(由操作系统内核进行管理,例如CFMessagePort消息)。官方也指出可以自定义Source,因此对于CFRunLoopSourceRef来说它更像一种协议,框架已经默认定义了两种实现,如果有必要开发人员也可以自定义。官方文档
CFRunLoopObserver
CFRunLoopObserver是观察者,可以观察RunLoop的各种状态,并抛出回调。
struct __CFRunLoopObserver {
CFRuntimeBase _base;
pthread_mutex_t _lock;
CFRunLoopRef _runLoop;
CFIndex _rlCount;
CFOptionFlags _activities;
CFIndex _order;
CFRunLoopObserverCallBack _callout;
CFRunLoopObserverContext _context;
}
CFRunLoopObserver可以观察的状态有如下6种:
/Run loop Observer Activities/
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), // 即将进入runloop
kCFRunLoopBeforeTimers = (1UL << 1), // 即将处理timer
kCFRunLoopBeforeSources = (1UL << 2), // 即将处理source
kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠
kCFRunLoopAfterWaiting = (1UL << 6), // 被唤醒但是还没有处理事件
kCFRunLoopExit = (1UL << 7), // run loop已经退出
kCFRunLoopAllActivities = 0x0FFFFFFFU
}
RunLoop通过监控Source来决定有没有任务要做,除此之外,我们还可以用RunLoop Observer来监控RunLoop本身的状态。RunLoop Observer可以监控上面的RunLoop事件,具体流程如下:

CFRunLoopTimer
CFRunLoopTimer是定时器,可以在设定的时间点抛出回调,它的结构如下:
struct __CFRunLoopTimer {
CFRuntimeBase _base;
uint16_t _bits; // 标记fire状态
pthread_mutex_t _lock;
CFRunLoopRef _runLoop; // 添加该Timer的runloop
CFMutableSetRef _rlModes; // 存放所有 包含该timer的mode的modeName,意味着一个timer可能会在多个mode中存在
CFAbsoluteTime _nextFireDate;
CFTimeInterval _interval; // 理想时间间隔
CFTimeInterval _tolerance; // 时间偏差
uint64_t _fireTSR;
CFIndex _order;
CFRunLoopTimerCallBack _callout;
CFRunLoopTimerContext _context;
}
CFRunLoopTimer具有以下特性:
- CFRunLoopTimer是定时器,可以在设定的时间点抛出回调
- CFRunLoopTimer和NSTimer是toll-free bridged的,可以相互转换
RunLoop实现
- 获取RunLoop
- 添加Mode
- 添加Run Loop Source
获取RunLoop
苹果官方不允许直接创建RunLoop对象,只能通过以下几个函数来获取RunLoop:
- CFRunLoopRef CFRunLoopGetCurrent(void)
- CFRunLoopRef CFRunLoopGetMain(void)
- (NSRunLoop *)currentRunLoop
- (NSRunLoop *)mainRunLoop
前两个是Core Foundation中的API,后两个是Foundation中的API。
CFRunLoopGetCurrent
// 获取当前所有线程的RunLoop
CFRunLoopRef CFRunLoopGetCurrent(void) {
CHECK_FOR_FORK();
CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
if (rl) return rl;
// 传入当前线程
return _CFRunLoopGet0(pthread_self());
}
在CFRunLoopGetCurrent函数内部调用了_CFRunLoopGet0(),传入的参数是当前线程pthread_self()。这里可以看出,CFRunLoopGetCurrent函数必须要在线程内部调用才能获取到当前线程的RunLoop。所以说线程的RunLoop必须要在子线程内部获取。
CFRunLoopGetMain
// 获取主线程的RunLoop
CFRunLoopRef CFRunLoopGetMain(void) {
CHECK_FOR_FORK();
static CFRunLoopRef __main = NULL;
// 传入主线程
if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np());
return __main;
}
在CFRunLoopGetMain函数内部也调用了_CFRunLoopGet0(),传入参数是主线程pthread_main_thread_np()。可以看出,CFRunLoopGetMain()不管在主线程还是子线程调用,都可以获取到主线程的RunLoop。
CFRunLoopGet0
前面两个函数都是使用了CFRunLoopGet0实现传入线程的函数,下面是CFRunLoopGet0的结构:
static CFMutableDictionaryRef __CFRunLoops = NULL;
static CFSpinLock_t loopsLock = CFSpinLockInit;
// t == 0 is a synonym for "main thread" that always works
// 根据线程取RunLoop
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
if (pthread_equal(t, kNilPthreadT)) {
t = pthread_main_thread_np();
}
__CFSpinLock(&loopsLock);
// 如果存储RunLoop的字典不存在
if (!__CFRunLoops) {
__CFSpinUnlock(&loopsLock);
// 创建一个临时字典dict
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
// 创建主线程的RunLoop
CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
// 把主线程的RunLoop保存到dict中,key是线程,value是Runloop
CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
// 此处NULL和__CFRunLoops指针都指向NULL,匹配,所以将dict写到__CFRunLoops
if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
// 释放dict
CFRelease(dict);
}
// 释放mainRunloop
CFRelease(mainLoop);
__CFSpinLock(&loopsLock);
}
// 以上说明,第一次进来的时候,不管是getMainRunLoop还是get子线程的runloop,主线程的runloop总是会被创建
// 从字典__CFRunLoops中获取传入线程t的runloop
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
__CFSpinUnlock(&loopsLock);
// 如果没有获取到
if (!loop) {
// 根据线程t创建一个runloop
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
__CFSpinLock(&loopsLock);
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, threadPointer(t));
if (!loop) {
// 把newLoop存入字典__CFRunLoops,key是线程t
CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
loop = newLoop;
}
__CFSpinUnlock(&loopsLock);
CFRelease(newLoop);
}
// 如果传入线程就是当前线程
if (pthread_equal(t, pthread_self())) {
_CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL);
if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) {
// 注册一个回调,当线程销毁时,销毁对应的RunLoop
_CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1),(void (*)(void *))__CFFinalizeRunLoop);
}
}
}
由代码可知:
* RunLoop和线程的一一对应的,对应的方式是以key-value的方式保存在一个全局字典中
* 主线程的RunLoop会在初始化全局字典时创建
* 子线程的RunLoop会在第一次获取的时候创建,如果不获取的话就一直不会被创建
* RunLoop会在线程销毁时销毁
添加Mode
在Core Foundation中,针对Mode的操作,苹果只开放了一下3个API(Cocoa中也有功能一样的函数)
- CFRunLoopAddCommonMode(CFRunLoopRef rl, CFStringRef mode)
- CFStringRef CFRunLoopCopyCurrentMode(CFRunLoopRef rl)
- CFArrayRef CFRunLoopCopyAllModes(CFRunLoopRef rl)
CFRunLoopAddCommonMode
Adds a mode to the set of run loop common modes.
向当前RunLoop的common modes中添加一个mode。CFRunLoopCopyCurrentMode
Returns the name of the mode in which a given run loop is currently running.
返回当前运行的mode的nameCFRunLoopCopyAllModes
Returns an array that contains all the defined modes for a CFRunLoop object.
返回当前RunLoop的所有mode
我们没有办法直接创建一个CFRunLoopMode对象,但是我们可以调用CFRunLoopAddCommonMode传入一个字符串向RunLoop中添加Mode,传入的字符串即为Mode的名字,Mode对象应该是此时在RunLoop内部创建的。
CFRunLoopAddCommonMode
void CFRunLoopAddCommonMode(CFRunLoopRef el, CFStringRef modeName) {
CHECK_FOR_FORK();
if (__CFRunLoopIsDeallocating(rl)) return;
__CFRunLoopLock(rl);
// 看rl中是否已经有这个mode,如果有就什么都不做
if (!CFSetContainsValue(rl->_commonModes, modeName)) {
CFSetRef set = rl->_commonModeItems ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModeItems) : NULL;
// 把modeName添加到Runloop的_commonModes中
CFSetAddValue(rl->_commonModes, modeName);
if (NULL != set) {
CFTypeRef context[2] = {rl, modeName};
// 这里调用CFRunLoopAddSource/CFRunLoopAddObserver/CFRunLoopAddTimer的时候会调用
// __CFRunLoopFindMode(rl, modeName, true),CFRunLoopMode对象在这个时候被创建
CFSetApplyFunction(set, (__CFRunLoopAddItemsToCommonMode), (void *)context);
CFRelease(set);
}
} else {
}
__CFRunLoopUnlock(rl);
}
- modeName不能重复,modeName是mode的唯一标识符
- RunLoop的_commonModes数组存放所有被标记为common的mode的名称
- 添加commonMode会把commonModeItems数组中的所有source同步到新添加的mode中
- CFRunLoopMode对象在CFRunLoopAddItemsToCommonMode函数中调用
- CFRunLoopFindMode时被创建
CFRunLoopCopyCurrentMode/CFRunLoopCopyAllModes
CFRunLoopCopyCurrentMode和CFRunLoopCopyAllModes的内部逻辑比较简单,直接取RunLoop的_currentMode和_modes返回,就不贴源码了。
添加Run Loop Source (ModeItem)
我们可以通过以下接口添加/移除各种事件:
- void CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef mode)
- void CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef mode)
- void CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef mode)
- void CFRunLoopRemoveObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef mode)
- void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode)
- void CFRunLoopRemoveTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode)
CFRunLoopAddSource
// 添加source事件
void CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef rls, CFStringRef modeName) {
CHECK_FOR_FORK();
if (__CFRunLoopIsDeallocating(rl)) return;
if (!__CFIsValid(rls)) return;
Boolean doVer0Callout = false;
__CFRunLoopLock(rl);
// 如果是kCFRunLoopCommonModes
if (modeName == kCFRunLoopCommonModes) {
// 如果runloop的_commonModes存在,则copy一个新的复制给set
CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
// 如果runl _commonModeItems为空
if (NULL == rl->_commonModeItems) {
// 先初始化
rl -> _commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
}
// 把传入的CFRunLoopSourceRef加入_commonModeItems
CFSetAddValue(rl->_commonModeItems, rls);
// 如果刚才set copy到的数组里面有数据
if (NULL != set) {
CFTypeRef context[2] = {rl, rls};
// 则把Set里面所有mode都执行一遍__CFRunLoopAddItemToCommonModes函数
CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
CFRelease(set);
}
//以上分支的逻辑就是,如果你往kCFRunLoopCommonModes里面添加一个source,那么所有_commonModes里的mode都会添加这个source
}else {
//根据modeName查找mode
CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, true);
//如果_sources0不存在,则初始化_sources0,_sources0和_portToV1SourceMap
if (NULL != rlm && NULL == rlm->_sources0) {
rlm->_sources0 = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
rlm->_sources1 = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
rlm->_portToV1SourceMap = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, NULL);
}
//如果_sources0和_sources1中都不包含传入的source
if (NULL != rlm && !CFSetContainsValue(rlm->_sources0, rls) && !CFSetContainsValue(rlm->_sources1, rls)) {
//如果version是0,则加到_sources0
if (0 == rls->_context.version0.version) {
CFSetAddValue(rlm->_sources0, rls);
//如果version是1,则加到_sources1
} else if (1 == rls->_context.version0.version) {
CFSetAddValue(rlm->_sources1, rls);
__CFPort src_port = rls->_context.version1.getPort(rls->_context.version1.info);
if (CFPORT_NULL != src_port) {
//此处只有在加到source1的时候才会把souce和一个mach_port_t对应起来
//可以理解为,source1可以通过内核向其端口发送消息来主动唤醒runloop
CFDictionarySetValue(rlm->_portToV1SourceMap, (const void *)(uintptr_t)src_port, rls);
__CFPortSetInsert(src_port, rlm->_portSet);
}
}
__CFRunLoopSourceLock(rls);
//把runloop加入到source的_runLoops中
if (NULL == rls->_runLoops) {
rls->_runLoops = CFBagCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeBagCallBacks); // sources retain run loops!
}
CFBagAddValue(rls->_runLoops, rl);
__CFRunLoopSourceUnlock(rls);
if (0 == rls->_context.version0.version) {
if (NULL != rls->_context.version0.schedule) {
doVer0Callout = true;
}
}
}
if (NULL != rlm) {
__CFRunLoopModeUnlock(rlm);
}
}
__CFRunLoopUnlock(rl);
if (doVer0Callout) {
// although it looses some protection for the source, we have no choice but
// to do this after unlocking the run loop and mode locks, to avoid deadlocks
// where the source wants to take a lock which is already held in another
// thread which is itself waiting for a run loop/mode lock
rls->_context.version0.schedule(rls->_context.version0.info, rl, modeName); /* CALLOUT */
}
}
CFRunLoopRemoveSource
//移除source
void CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef rls, CFStringRef modeName) { /* DOES CALLOUT */
CHECK_FOR_FORK();
Boolean doVer0Callout = false, doRLSRelease = false;
__CFRunLoopLock(rl);
//如果是kCFRunLoopCommonModes,则从_commonModes的所有mode中移除该source
if (modeName == kCFRunLoopCommonModes) {
if (NULL != rl->_commonModeItems && CFSetContainsValue(rl->_commonModeItems, rls)) {
CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
CFSetRemoveValue(rl->_commonModeItems, rls);
if (NULL != set) {
CFTypeRef context[2] = {rl, rls};
/* remove new item from all common-modes */
CFSetApplyFunction(set, (__CFRunLoopRemoveItemFromCommonModes), (void *)context);
CFRelease(set);
}
} else {
}
} else {
//根据modeName查找mode,如果不存在,返回NULL
CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, false);
if (NULL != rlm && ((NULL != rlm->_sources0 && CFSetContainsValue(rlm->_sources0, rls)) || (NULL != rlm->_sources1 && CFSetContainsValue(rlm->_sources1, rls)))) {
CFRetain(rls);
//根据source版本做对应的remove操作
if (1 == rls->_context.version0.version) {
__CFPort src_port = rls->_context.version1.getPort(rls->_context.version1.info);
if (CFPORT_NULL != src_port) {
CFDictionaryRemoveValue(rlm->_portToV1SourceMap, (const void *)(uintptr_t)src_port);
__CFPortSetRemove(src_port, rlm->_portSet);
}
}
CFSetRemoveValue(rlm->_sources0, rls);
CFSetRemoveValue(rlm->_sources1, rls);
__CFRunLoopSourceLock(rls);
if (NULL != rls->_runLoops) {
CFBagRemoveValue(rls->_runLoops, rl);
}
__CFRunLoopSourceUnlock(rls);
if (0 == rls->_context.version0.version) {
if (NULL != rls->_context.version0.cancel) {
doVer0Callout = true;
}
}
doRLSRelease = true;
}
if (NULL != rlm) {
__CFRunLoopModeUnlock(rlm);
}
}
__CFRunLoopUnlock(rl);
if (doVer0Callout) {
// although it looses some protection for the source, we have no choice but
// to do this after unlocking the run loop and mode locks, to avoid deadlocks
// where the source wants to take a lock which is already held in another
// thread which is itself waiting for a run loop/mode lock
rls->_context.version0.cancel(rls->_context.version0.info, rl, modeName); /* CALLOUT */
}
if (doRLSRelease) CFRelease(rls);
}
添加Observer和Timer
添加observer和timer的内部逻辑和添加source大体类似。
区别在于observer和timer只能被添加到一个RunLoop的一个或者多个mode中,比如一个timer被添加到主线程的RunLoop中,这不能再把该timer添加到子线程的RunLoop,而source没有这个限制,不管是哪个RunLoop,只要mode中没有,就可以添加。
CFRunLoopSource结构体中有保存RunLoop对象的数组,而CFRunLoopObserver和CFRunLoopTimer只有单个RunLoop对象。
RunLoop运行

在Core Foundation中通过下面两个API让RunLoop运行:
- void CFRunLoopRun(void)
在默认的mode下运行当前线程的RunLoop。
- CFRunLoopRunResult CFRunLoopRunInMode(CFStringRef mode, CFTimeInterval seconds, Boolean returnAfterSourceHandled)
在指定mode下运行当前线程的RunLoop。
CFRunLoopRun
// 默认运行runLoop的kCFRunloopDefaultMode
void CFRunLoopRun(void) {
int32_t result;
do {
// 默认在kCFRunLoopDefaultMode下运行runloop
result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
CHECK_FOR_FORK();
} while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}
在CFRunLoopRun函数中调用了CFRunLoopRunSpecific函数,runloop参数传入当前RunLoop对象,modeName参数传入kCFRunLoopDefaultMode。
CFRunLoopRunInMode
SInt32 CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {
CHECK_FOR_FORK();
return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled);
}
在CFRunloopRunInMode函数中也调用了CFRunLoopRunSpecific函数,runloop参数传入当前RunLoop对象,modeName参数继续传递CFRunLoopRunInMode传入modeName。
CFRunLoopRunInMode
SInt32 CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {
CHECK_FOR_FORK();
return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled);
}
在CFRunLoopRunInMode函数中也调用了CFRunLoopRunSpecific函数,runloop参数传入当前RunLoop对象,modeName参数继续传递CFRunLoopRunInMode传入modeName。
虽然Runloop有很多mode,但是RunLoop在run的时候必须只能指定其中一个mode,运行起来之后,被指定的mode即为currentMode。
CFRunLoopRunSpecific
/*
* 指定mode运行runloop
* @param rl 当前运行的runloop
* @param modeName 需要运行的mode的name
* @param seconds runloop的超时时间
* @param returnAfterSourceHandled 是否处理完事件就返回
*/
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */
CHECK_FOR_FORK();
if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished;
__CFRunLoopLock(rl);
//根据modeName找到本次运行的mode
CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
//如果没找到 || 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;
// 1.通知observer即将进入runloop
if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
//10.通知observer已退出runloop
if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
__CFRunLoopModeUnlock(currentMode);
__CFRunLoopPopPerRunData(rl, previousPerRun);
rl->_currentMode = previousMode;
__CFRunLoopUnlock(rl);
return result;
}
- 如果指定了一个不存在的mode来运行RunLoop,那么会失败,mode不会被创建,所以这里传入的mode必须存在的
- 如果指定了一个mode,但是这个mode中不包含任何modeItem,那么RunLoop也不会运行,所以必须要传入至少包含一个modeItem的mode
- 在进入runloop之前通知observer,状态为kCFRunLoopEntry
- 在退出runloop之后通知observer,状态为kCFRunLoopExit
RunLoop的运行的最核心函数是__CFRunLoopRun,接下来我们分析__CFRunLoopRun的源码。
__CFRunLoopRun
/**
* 运行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或者mode是stop状态,则直接return,不进入循环
if (__CFRunLoopIsStopped(rl)) {
__CFRunLoopUnsetStopped(rl);
return kCFRunLoopRunStopped;
} else if (rlm->_stopped) {
rlm->_stopped = false;
return kCFRunLoopRunStopped;
}
//mach端口,在内核中,消息在端口之间传递。 初始为0
mach_port_name_t dispatchPort = MACH_PORT_NULL;
//判断是否为主线程
Boolean libdispatchQSafe = pthread_main_np() && ((HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY && NULL == previousMode) || (!HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY && 0 == _CFGetTSD(__CFTSDKeyIsInGCDMainQ)));
//如果在主线程 && runloop是主线程的runloop && 该mode是commonMode,则给mach端口赋值为主线程收发消息的端口
if (libdispatchQSafe && (CFRunLoopGetMain() == rl) && CFSetContainsValue(rl->_commonModes, rlm->_name)) dispatchPort = _dispatch_get_main_queue_port_4CF();
#if USE_DISPATCH_SOURCE_FOR_TIMERS
mach_port_name_t modeQueuePort = MACH_PORT_NULL;
if (rlm->_queue) {
//mode赋值为dispatch端口_dispatch_runloop_root_queue_perform_4CF
modeQueuePort = _dispatch_runloop_root_queue_get_port_4CF(rlm->_queue);
if (!modeQueuePort) {
CRASH("Unable to get port for run loop mode queue (%d)", -1);
}
}
#endif
//GCD管理的定时器,用于实现runloop超时机制
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;
}
//seconds为超时时间,超时时执行__CFRunLoopTimeout函数
else if (seconds <= TIMER_INTERVAL_LIMIT) {
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 {
//初始化一个存放内核消息的缓冲池
uint8_t msg_buffer[3 * 1024];
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
mach_msg_header_t *msg = NULL;
mach_port_t livePort = MACH_PORT_NULL;
#elif DEPLOYMENT_TARGET_WINDOWS
HANDLE livePort = NULL;
Boolean windowsMessageReceived = false;
#endif
//取所有需要监听的port
__CFPortSet waitSet = rlm->_portSet;
//设置RunLoop为可以被唤醒状态
__CFRunLoopUnsetIgnoreWakeUps(rl);
//2.通知observer,即将触发timer回调,处理timer事件
if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
//3.通知observer,即将触发Source0回调
if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
//执行加入当前runloop的block
__CFRunLoopDoBlocks(rl, rlm);
//4.处理source0事件
//有事件处理返回true,没有事件返回false
Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
if (sourceHandledThisLoop) {
//执行加入当前runloop的block
__CFRunLoopDoBlocks(rl, rlm);
}
//如果没有Sources0事件处理 并且 没有超时,poll为false
//如果有Sources0事件处理 或者 超时,poll都为true
Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR);
//第一次do..whil循环不会走该分支,因为didDispatchPortLastTime初始化是true
if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime) {
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
//从缓冲区读取消息
msg = (mach_msg_header_t *)msg_buffer;
//5.接收dispatchPort端口的消息,(接收source1事件)
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0)) {
//如果接收到了消息的话,前往第9步开始处理msg
goto handle_msg;
}
#elif DEPLOYMENT_TARGET_WINDOWS
if (__CFRunLoopWaitForMultipleObjects(NULL, &dispatchPort, 0, 0, &livePort, NULL)) {
goto handle_msg;
}
#endif
}
didDispatchPortLastTime = false;
//6.通知观察者RunLoop即将进入休眠
if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
//设置RunLoop为休眠状态
__CFRunLoopSetSleeping(rl);
// do not do any user callouts after this point (after notifying of sleeping)
// Must push the local-to-this-activation ports in on every loop
// iteration, as this mode could be run re-entrantly and we don't
// want these ports to get serviced.
__CFPortSetInsert(dispatchPort, waitSet);
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
#if USE_DISPATCH_SOURCE_FOR_TIMERS
//这里有个内循环,用于接收等待端口的消息
//进入此循环后,线程进入休眠,直到收到新消息才跳出该循环,继续执行run loop
do {
if (kCFUseCollectableAllocator) {
objc_clear_stack(0);
memset(msg_buffer, 0, sizeof(msg_buffer));
}
msg = (mach_msg_header_t *)msg_buffer;
//7.接收waitSet端口的消息
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY);
//收到消息之后,livePort的值为msg->msgh_local_port,
if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
// Drain the internal queue. If one of the callout blocks sets the timerFired flag, break out and service the timer.
while (_dispatch_runloop_root_queue_perform_4CF(rlm->_queue));
if (rlm->_timerFired) {
// Leave livePort as the queue port, and service timers below
rlm->_timerFired = false;
break;
} else {
if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);
}
} else {
// Go ahead and leave the inner loop.
break;
}
} while (1);
#else
if (kCFUseCollectableAllocator) {
objc_clear_stack(0);
memset(msg_buffer, 0, sizeof(msg_buffer));
}
msg = (mach_msg_header_t *)msg_buffer;
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY);
#endif
#elif DEPLOYMENT_TARGET_WINDOWS
// Here, use the app-supplied message queue mask. They will set this if they are interested in having this run loop receive windows messages.
__CFRunLoopWaitForMultipleObjects(waitSet, NULL, poll ? 0 : TIMEOUT_INFINITY, rlm->_msgQMask, &livePort, &windowsMessageReceived);
#endif
__CFRunLoopLock(rl);
__CFRunLoopModeLock(rlm);
// Must remove the local-to-this-activation ports in on every loop
// iteration, as this mode could be run re-entrantly and we don't
// want these ports to get serviced. Also, we don't want them left
// in there if this function returns.
__CFPortSetRemove(dispatchPort, waitSet);
__CFRunLoopSetIgnoreWakeUps(rl);
// user callouts now OK again
//取消runloop的休眠状态
__CFRunLoopUnsetSleeping(rl);
//8.通知观察者runloop被唤醒
if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
//9.处理收到的消息
handle_msg:;
__CFRunLoopSetIgnoreWakeUps(rl);
#if DEPLOYMENT_TARGET_WINDOWS
if (windowsMessageReceived) {
// These Win32 APIs cause a callout, so make sure we're unlocked first and relocked after
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
if (rlm->_msgPump) {
rlm->_msgPump();
} else {
MSG msg;
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_NOYIELD)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
__CFRunLoopLock(rl);
__CFRunLoopModeLock(rlm);
sourceHandledThisLoop = true;
// To prevent starvation of sources other than the message queue, we check again to see if any other sources need to be serviced
// Use 0 for the mask so windows messages are ignored this time. Also use 0 for the timeout, because we're just checking to see if the things are signalled right now -- we will wait on them again later.
// NOTE: Ignore the dispatch source (it's not in the wait set anymore) and also don't run the observers here since we are polling.
__CFRunLoopSetSleeping(rl);
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
__CFRunLoopWaitForMultipleObjects(waitSet, NULL, 0, 0, &livePort, NULL);
__CFRunLoopLock(rl);
__CFRunLoopModeLock(rlm);
__CFRunLoopUnsetSleeping(rl);
// If we have a new live port then it will be handled below as normal
}
#endif
if (MACH_PORT_NULL == livePort) {
CFRUNLOOP_WAKEUP_FOR_NOTHING();
// handle nothing
//通过CFRunloopWake唤醒
} else if (livePort == rl->_wakeUpPort) {
CFRUNLOOP_WAKEUP_FOR_WAKEUP();
//什么都不干,跳回2重新循环
// do nothing on Mac OS
#if DEPLOYMENT_TARGET_WINDOWS
// Always reset the wake up port, or risk spinning forever
ResetEvent(rl->_wakeUpPort);
#endif
}
#if USE_DISPATCH_SOURCE_FOR_TIMERS
//如果是定时器事件
else if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
CFRUNLOOP_WAKEUP_FOR_TIMER();
//9.1 处理timer事件
if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
// Re-arm the next timer, because we apparently fired early
__CFArmNextTimerInMode(rlm, rl);
}
}
#endif
#if USE_MK_TIMER_TOO
//如果是定时器事件
else if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) {
CFRUNLOOP_WAKEUP_FOR_TIMER();
// On Windows, we have observed an issue where the timer port is set before the time which we requested it to be set. For example, we set the fire time to be TSR 167646765860, but it is actually observed firing at TSR 167646764145, which is 1715 ticks early. The result is that, when __CFRunLoopDoTimers checks to see if any of the run loop timers should be firing, it appears to be 'too early' for the next timer, and no timers are handled.
// In this case, the timer port has been automatically reset (since it was returned from MsgWaitForMultipleObjectsEx), and if we do not re-arm it, then no timers will ever be serviced again unless something adjusts the timer list (e.g. adding or removing timers). The fix for the issue is to reset the timer here if CFRunLoopDoTimers did not handle a timer itself. 9308754
//9.1处理timer事件
if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
// Re-arm the next timer
__CFArmNextTimerInMode(rlm, rl);
}
}
#endif
//如果是dispatch到main queue的block
else if (livePort == dispatchPort) {
CFRUNLOOP_WAKEUP_FOR_DISPATCH();
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
_CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)6, NULL);
#if DEPLOYMENT_TARGET_WINDOWS
void *msg = 0;
#endif
//9.2执行block
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
_CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)0, NULL);
__CFRunLoopLock(rl);
__CFRunLoopModeLock(rlm);
sourceHandledThisLoop = true;
didDispatchPortLastTime = true;
} else {
CFRUNLOOP_WAKEUP_FOR_SOURCE();
// Despite the name, this works for windows handles as well
CFRunLoopSourceRef rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort);
// 有source1事件待处理
if (rls) {
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
mach_msg_header_t *reply = NULL;
//9.2 处理source1事件
sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop;
if (NULL != reply) {
(void)mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
CFAllocatorDeallocate(kCFAllocatorSystemDefault, reply);
}
#elif DEPLOYMENT_TARGET_WINDOWS
sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls) || sourceHandledThisLoop;
#endif
}
}
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);
#endif
__CFRunLoopDoBlocks(rl, rlm);
if (sourceHandledThisLoop && stopAfterHandle) {
//进入run loop时传入的参数,处理完事件就返回
retVal = kCFRunLoopRunHandledSource;
}else if (timeout_context->termTSR < mach_absolute_time()) {
//run loop超时
retVal = kCFRunLoopRunTimedOut;
}else if (__CFRunLoopIsStopped(rl)) {
//run loop被手动终止
__CFRunLoopUnsetStopped(rl);
retVal = kCFRunLoopRunStopped;
}else if (rlm->_stopped) {
//mode被终止
rlm->_stopped = false;
retVal = kCFRunLoopRunStopped;
}else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
//mode中没有要处理的事件
retVal = kCFRunLoopRunFinished;
}
//除了上面这几种情况,都继续循环
} while (0 == retVal);
if (timeout_timer) {
dispatch_source_cancel(timeout_timer);
dispatch_release(timeout_timer);
} else {
free(timeout_context);
}
return retVal;
}
__CFRunLoopServiceMachPort
/**
* 接收指定内核端口的消息
*
* @param port 接收消息的端口
* @param buffer 消息缓冲区
* @param buffer_size 消息缓冲区大小
* @param livePort 暂且理解为活动的端口,接收消息成功时候值为msg->msgh_local_port,超时时为MACH_PORT_NULL
* @param timeout 超时时间,单位是ms,如果超时,则RunLoop进入休眠状态
*
* @return 接收消息成功时返回true 其他情况返回false
*/
static Boolean __CFRunLoopServiceMachPort(mach_port_name_t port, mach_msg_header_t **buffer, size_t buffer_size, mach_port_t *livePort, mach_msg_timeout_t timeout) {
Boolean originalBuffer = true;
kern_return_t ret = KERN_SUCCESS;
for (;;) { /* In that sleep of death what nightmares may come ... */
mach_msg_header_t *msg = (mach_msg_header_t *)*buffer;
msg->msgh_bits = 0; //消息头的标志位
msg->msgh_local_port = port; //源(发出的消息)或者目标(接收的消息)
msg->msgh_remote_port = MACH_PORT_NULL; //目标(发出的消息)或者源(接收的消息)
msg->msgh_size = buffer_size; //消息缓冲区大小,单位是字节
msg->msgh_id = 0; //唯一id
if (TIMEOUT_INFINITY == timeout) { CFRUNLOOP_SLEEP(); } else { CFRUNLOOP_POLL(); }
//通过mach_msg发送或者接收的消息都是指针,
//如果直接发送或者接收消息体,会频繁进行内存复制,损耗性能
//所以XNU使用了单一内核的方式来解决该问题,所有内核组件都共享同一个地址空间,因此传递消息时候只需要传递消息的指针
ret = mach_msg(msg,
MACH_RCV_MSG|MACH_RCV_LARGE|((TIMEOUT_INFINITY != timeout) ? MACH_RCV_TIMEOUT : 0)|MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0)|MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AV),
0,
msg->msgh_size,
port,
timeout,
MACH_PORT_NULL);
CFRUNLOOP_WAKEUP(ret);
//接收/发送消息成功,给livePort赋值为msgh_local_port
if (MACH_MSG_SUCCESS == ret) {
*livePort = msg ? msg->msgh_local_port : MACH_PORT_NULL;
return true;
}
//MACH_RCV_TIMEOUT
//超出timeout时间没有收到消息,返回MACH_RCV_TIMED_OUT
//此时释放缓冲区,把livePort赋值为MACH_PORT_NULL
if (MACH_RCV_TIMED_OUT == ret) {
if (!originalBuffer) free(msg);
*buffer = NULL;
*livePort = MACH_PORT_NULL;
return false;
}
//MACH_RCV_LARGE
//如果接收缓冲区太小,则将过大的消息放在队列中,并且出错返回MACH_RCV_TOO_LARGE,
//这种情况下,只返回消息头,调用者可以分配更多的内存
if (MACH_RCV_TOO_LARGE != ret) break;
//此处给buffer分配更大内存
buffer_size = round_msg(msg->msgh_size + MAX_TRAILER_SIZE);
if (originalBuffer) *buffer = NULL;
originalBuffer = false;
*buffer = realloc(*buffer, buffer_size);
}
HALT;
return false;
}
与线程
一个线程一次只能执行一个任务,执行完成后就会退出。Runloop机制能让线程随时处理事件而不退出,程序需要运行时持续运行,不需要运行时进入休眠状态。
不允许直接创建。CFRunLoopGetMain() GetCurrent()线程与RunLoop一一对应关系保存在之后的Dictionary中。RunLoop对象在第一次获取时创建在线程结束时销毁,主线程RunLoop默认创建子线程需主动获取。
主线程中 UIApplicationMain函数内开启了主线程RunLoop并执行了一段无限循环的代码。主函数不会return,因为UIApplication主任务未完成。
网友评论