/**
* 运行run loop
*
* @param rl 运行的RunLoop对象
* @param modeName 运行的mode名称
* @param seconds run loop超时时间
* @param returnAfterSourceHandled true:run loop处理完事件就退出 false:一直运行直到超时或者被手动终止
*
*
* @return 返回4种状态
*/
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled){
// 根据modeName获得当前mode
CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
// 保存上一次mode 并将runloop替换为当前mode
CFRunLoopModeRef previousMode = rl->_currentMode;
rl->_currentMode = currentMode;
int32_t result = kCFRunLoopRunFinished;
if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry); // 通知observer,进入RunLoop
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode); // 调用 __CFRunLoopRun 真正开始run
if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit); // 通知observer, runloop退出
rl->_currentMode = previousMode; // 将runloop恢复为之前的mode
return result; // 返回runloop run的返回值
}
学习参考:https://blog.csdn.net/u013378438/article/details/80239686
一、runloop的组成
每个线程对应1个runloop
每个runloop只能在一个mode下运行
每个mode有很多事件源,timer,observer
1.runloop在run的时候
1.通知Observer,即将进入runloop。 __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
2.通知Observer,即将处理Timer。__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
3.通知Observer,即将处理source0。__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources)
4.处理source0。if (__CFRunLoopDoSources0(rl, rlm, stopAfterHandle)) __CFRunLoopDoBlocks(rl, rlm);
5.如果有source1,跳到9。if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL))
6.通知Observer,即将休眠。__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
7.休眠,等待唤醒 -->(source1唤醒,Timer唤醒,runloop超时,外部手动唤醒)
(i)开始休眠,监听waitSet所置顶的mach port。__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
8.通知Observer,被唤醒。__CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
9.处理唤醒时收到的消息,之后调回2
10.通知Observer,即将退出runloop。__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit)
2.runloop的结构
runloop被唤醒通知app做下一步任务时的回调函数,大多数是以下6个
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__
__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__
__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__
__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__
Runloop对应的数据结构为
// Foundation:
NSRunLoop : NSObject
// Core Foundation:
struct __CFRunLoop
__CFRunLoop * CFRunLoopRef
struct __CFRunLoop {
__CFPort _wakeUpPort; // 唤醒runloop的pot
CFMutableSetRef _commonModes;//被加入到common mode中的mode
CFMutableSetRef _commonModeItems;//mode下的items(source/timer/obserer)
CFRunLoopModeRef _currentMode;//当前的mode
CFMutableSetRef _modes;//Runloop所有的modes
...
};
当runloop没有任务处理,休眠时,在xcode点击暂定,会看到主线程停留在mach_msg_trap(),这个就是runloop接收消息的函数,如果没有port送消息过来,runloop就继续等待。
二、Thread & Runloop
每个Thread都有一个Runloop,但是除了系统会为主线程自动创建一个Runloop,子线程需要手动获取runloop,并且是懒加载的
NSRunLoop currentRunLoop];
[NSRunLoop mainRunLoop]
// 其对应的CF源码为:
CF_EXPORT CFRunLoopRef CFRunLoopGetCurrent(void);
CF_EXPORT CFRunLoopRef CFRunLoopGetMain(void);
CFRunLoopRef CFRunLoopGetMain(void) {
CHECK_FOR_FORK();
static CFRunLoopRef __main = NULL; // 主线程会存在一个静态变量
if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np()); // no CAS needed
return __main;
}
CFRunLoopRef CFRunLoopGetCurrent(void) {
CHECK_FOR_FORK();
CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);//子线程会存在TSD(线程私有数据)
if (rl) return rl;
return _CFRunLoopGet0(pthread_self());
}
这两个函数最终都会调用_CFRunLoopGet0(pthread_t t)创建线程,看一下这个方法实现了什么
static CFMutableDictionaryRef __CFRunLoops = NULL;//全局变量,存储线程对应的runloop
static CFLock_t loopsLock = CFLockInit;
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
...
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t)); // 在静态全局变量__CFRunLoops寻找线程t所对应的RunLoop(key为线程自身)
__CFUnlock(&loopsLock);
if (!loop) { // 没找到
CFRunLoopRef newLoop = __CFRunLoopCreate(t); // 创建一个新的Loop
__CFLock(&loopsLock);
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t)); // 为什么get两次?这里猜测是因为保证线程安全,保证一个线程只有一个唯一的runloop。
if (!loop) {
CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop); // 将新创建的Loop存入__CFRunLoops字典
loop = newLoop;
}
// don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it
__CFUnlock(&loopsLock);
CFRelease(newLoop);
}
if (pthread_equal(t, pthread_self())) { // 若当前调用线程和t是一个线程,则同时设置当前线程的TSD
_CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL);
if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) {
_CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop);
}
}
return loop; // 返回Loop
}
由代码可知
1.Runloop和Thread是一一对应的。CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
2.Thread默认是没有对应Runloop的,只有主动get的时候才会懒加载,创建一个Runloop
3.所有的Thread对应的Runloop都被存储在__CFRunLoops字典中,同时,主线程有个static CFRunLoopRef __main,子线程在TSD中存储,方便快速获取。
三、RunLoop Mode
RunLoop只有在Run的时候才会处理具体事务。每次run,都必须指定有个mode,mode指定了这次runloop可以处理的任务,对于不属于当前mode的任务,则需要切换mode重新调用run方法,才能被处理。
static int32_t __CFRunLoopRun(CFRunLoopRef rl, // runloop will run
CFRunLoopModeRef rlm, // the runloop 's mode to run
Boolean stopAfterHandle, // if Yes, exit runloop after handle on source
CFRunLoopModeRef previousMode) // previous runloop mode
RunLoop,RunLoop mode和RunLoop items的关系如下图所示
一个Runloop可以有多个mode,每个mode下有soure0、souce1、timer和observers,当runloop休眠时(mach_msg_trap状态),外部可以通过wakeUpPort和timerPort将runloop唤醒。
runloop的代码结构
struct __CFRunLoopMode {
CFRuntimeBase _base;
pthread_mutex_t _lock; /* must have the run loop locked before locking this */
CFStringRef _name;
Boolean _stopped;
char _padding[3];
CFMutableSetRef _sources0;
CFMutableSetRef _sources1;
CFMutableArrayRef _observers;
CFMutableArrayRef _timers;
CFMutableDictionaryRef _portToV1SourceMap;
__CFPortSet _portSet;
CFIndex _observerMask;
#if USE_DISPATCH_SOURCE_FOR_TIMERS
dispatch_source_t _timerSource;
dispatch_queue_t _queue; // dispatch timer 所在的queue
Boolean _timerFired; // set to true by the source when a timer has fired
Boolean _dispatchTimerArmed;
#endif
#if USE_MK_TIMER_TOO
mach_port_t _timerPort;
Boolean _mkTimerArmed;
#endif
#if DEPLOYMENT_TARGET_WINDOWS
DWORD _msgQMask;
void (*_msgPump)(void);
#endif
uint64_t _timerSoftDeadline; /* TSR */
uint64_t _timerHardDeadline; /* TSR */
};
回看CFRunLoop中的mode相关的结构:
struct __CFRunLoop {
...
CFMutableSetRef _commonModes; // set:被加入到common mode中的mode
CFMutableSetRef _commonModeItems; // set: 被加入到common mode 下的items(source/timer/observer )
CFRunLoopModeRef _currentMode; // 当前的mode
CFMutableSetRef _modes; // RunLoop所有的modes
...
};
一个普通的mode可以把自己标记为common,,做法是,系统将该mode的name添加到_commonModes中。
当item被加入到commonModes时,首先会在runloop的commonModeItems加入一个条目,然后遍历所有的_commonModes,将该item加入到已经标记为common的mode下。
当一个mode作为整体被加入commonModes下时,系统会调用void CFRunLoopAddCommonMode(CFRunLoopRef rl, CFStringRef modeName)方法,在方法内部,首先把mode置为common,把mode的name加入到commonModes,然后把commonItems中所有的元素添加到这个mode中,但mode中的item不会被加入到commonItems中,这意味着当mode作为整体加入到commonModes中时,mode可以相应commonModeItems事件,而本身自带的mode items,在别的被标记为common的mode中,却不会响应。
当我们将任务交给runloop时,需要制定哪个mode下处理,如果不制定,默认在default mode下处理。
一个任务可以提交多个mode,但向同一个mode多次提交同一个任务,则mode中仅会保存一个任务,代码中有类似判断
if(!CFSetContainsValue(rlm->_sources0, rls)) {
// 将任务加入到mode中
}
timer是基于Runloop实现的,我们在创建timer时,可以指定timer的mode,如果没指定,默认是在default mode下
NSTimer *timer = [NSTimer timerWithTimeInterval:1 repeats:NO block:^(NSTimer * _Nonnull timer) {
NSLog(@"do timer");
}];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; // 指定timer在common modes(default mode + event tracking mode) 下运行
四、RunLoop Source
RunLoop能够哦处理的事件分为Input source和timer。
Input source分为 source0和source1,均有结构体__CFRunLoopSource表示
struct __CFRunLoopSource {
CFRuntimeBase _base;
uint32_t _bits;
pthread_mutex_t _lock;
CFIndex _order; /* 优先级;越小,优先级越高。可以是负数。immutable */
CFMutableBagRef _runLoops;
union { // 联合,用于保存source的信息,同时可以区分source是0还是1类型
CFRunLoopSourceContext version0; /* immutable, except invalidation */
CFRunLoopSourceContext1 version1; /* immutable, except invalidation */
} _context;
};
typedef struct {
CFIndex version; // 类型:source0
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); // call out
} CFRunLoopSourceContext;
typedef struct {
CFIndex version; // 类型:source1
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);//比source0多了一个port,少了个cancel
void * (*perform)(void *msg, CFIndex size, CFAllocatorRef allocator, void *info);
#else
void * (*getPort)(void *info);
void (*perform)(void *info); // call out
#endif
} CFRunLoopSourceContext1;
soure0 VS souce1
相同点:
1.都是__CFRunLoopSource类型
2.都是要被Sinaled后,才被处理
3.处理时,都是调用__CFRunLoopSource._content.version(0?1).perform
不同
1.source0需要手动signaled,source1系统会自动signaled
2.source0需要手动唤醒RunLoop,才能被处理。CFRunLoopWakeUp(CFRunLoopRef rl)。而source1会通过mach port 自动唤醒RunLoop来处理。
3.source1 由RunLoop和内核管理,mach port驱动,source0则偏向应用层一些,如果UIEvent处理,会以souce0的形势发给Main RunLoop
Timer
我们经常用的timer有
1.NSTimer,performSelector:afterDelay(由RunLoop处理,内部结构为CFRunLoopTimerRef)
2.GCD Timer(由GCD自己实现,不通过RunLoop)
3.CADisplayLink(通过向RunLoop投递souce1实现回调)
NSTimer & PerformSelector:afterDelay:
NSTimer在CF源码中的结构
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 */ // timer 回调
CFRunLoopTimerContext _context; /* immutable, except invalidation */
};
1.当NSTimer被加入到RunLoop的某个mode下时,根据timer是否设置了tolerance,如果没有设置,调用底层的XNU内核的mk_timer注册一个mach-port,如果设置了tolerance,则注册一个GCD Timer。
2.当XNU内核或GCD管理的timer的fire time到了,通过对应的mach port,唤醒RunLoop对应的timePort或GCD queue port
3.RunLoop执行__CFRunLoopDoTimers,其中会调用__CFRunLoopDoTimer,DoTimer方法里会根据当前的mach time和Timer的fireTSR属性,判断fireTSR是否<当前的mach time,如果小于,调用__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__来回调到timer的fire函数。触发timer,并且更新下一次fire时间的fireTSR
timer事件触发时的函数堆栈:
PerformSelector:afterDealy
底层实现实质实当前线程的runloop的defaultMode下加了一个timer,因此,在没有runloop启动的线程下,该函数是无法使用的。
PerformSelectorOnThread(mainThread):
在目标线程的Default mode 下添加一个source0,在source0的回调中,会执行我们的selector,因此,在线程没有启动runloop的情况下,PerformSelectorOnThread也是不能生效的
dispatch to main queue
当调用 dispatch_async(dispatch_get_main_queue(), block) 时,libDispatch 会向主线程的 RunLoop 发送消息,RunLoop会被唤醒,并从消息中取得这个 block,并在回调 CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE() 里执行这个 block。但这个逻辑仅限于 dispatch 到主线程,dispatch 到其他线程仍然是由 libDispatch 处理的。
当我们在dispatch main queue block中下断点时,可以看到如下调用堆栈:
Observer
Observer在CF中的结构如下:
struct __CFRunLoopObserver {
CFRuntimeBase _base;
pthread_mutex_t _lock;
CFRunLoopRef _runLoop;
CFIndex _rlCount;
CFOptionFlags _activities; /*所监听的事件,通过位异或,可以监听多种事件 immutable */
CFIndex _order; /* 优先级 immutable */
CFRunLoopObserverCallBack _callout; /* observer 回调 immutable */
CFRunLoopObserverContext _context; /* immutable, except invalidation */
};
/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), // 进入RunLoop循环(这里其实还没进入)
kCFRunLoopBeforeTimers = (1UL << 1), // RunLoop 要处理timer了
kCFRunLoopBeforeSources = (1UL << 2), // RunLoop 要处理source了
kCFRunLoopBeforeWaiting = (1UL << 5), // RunLoop要休眠了
kCFRunLoopAfterWaiting = (1UL << 6), // RunLoop醒了
kCFRunLoopExit = (1UL << 7), // RunLoop退出(和kCFRunLoopEntry对应)
kCFRunLoopAllActivities = 0x0FFFFFFFU
};
Observer的作用是可以让外部监听RunLoop的运行状态,从而根据不同时机,做一些操作。系统会在APP启东时,向main RunLoop里注册了两个Observer,其回调都是_wrapRunLoopWithAutoreleasePollHandler().
第一个Observer监听的时间是Entry,其回调内会调用_objc_autoreleasePoolPush()创建自动释放池,其order是-2147483647,优先级最高,保证创建释放池发生再其他所有回调之前。
第二个Observer监视了两个事件:BeforeWaiting时调用_objc_autoreleasePoolPop()和_objc_autoreleasePoolPush(),释放旧池创建新池。Exit时调用_objc_autoreleasePoolPop()释放自动释放池。这个Observer的order是21477483647,优先级最低,保证其释放池发生在所有回调之后。
在主线程执行的代码,通常是写在诸如事件回调、timer回调内的。这些回调会被RunLoop创建好的AutoreleasePool环绕着,所以不会出现内存泄露,开发者也不必显示创建Pool了
五、RunLoop源码剖析
/**
* 运行run loop
*
* @param rl 运行的RunLoop对象
* @param modeName 运行的mode名称
* @param seconds run loop超时时间
* @param returnAfterSourceHandled true:run loop处理完事件就退出 false:一直运行直到超时或者被手动终止
*
*
* @return 返回4种状态
*/
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled){
// 根据modeName获得当前mode
CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
// 保存上一次mode 并将runloop替换为当前mode
CFRunLoopModeRef previousMode = rl->_currentMode;
rl->_currentMode = currentMode;
int32_t result = kCFRunLoopRunFinished;
if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry); // 通知observer,进入RunLoop
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode); // 调用 __CFRunLoopRun 真正开始run
if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit); // 通知observer, runloop退出
rl->_currentMode = previousMode; // 将runloop恢复为之前的mode
return result; // 返回runloop run的返回值
}
核心是__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,
CFRunLoopModeRef previousMode) {
// 借助GCD timer,设置runloop的超时时间
dispatch_source_t timeout_timer = ...
// 超时回调函数 __CFRunLoopTimeout
dispatch_source_set_event_handler_f(timeout_timer, __CFRunLoopTimeout);
// 超时取消函数 __CFRunLoopTimeoutCancel
dispatch_source_set_cancel_handler_f(timeout_timer, __CFRunLoopTimeoutCancel);
// 进入do while循环,开始run
int32_t retVal = 0; // runloop run返回值,默认为0,会在do while中根据情况被修改,当不为0时,runloop退出
do{
mach_port_t livePort = MACH_PORT_NULL; // 用于记录唤醒休眠的RunLoop的mach port,休眠前是NULL
__CFPortSet waitSet = rlm->_portSet; // 取当前mode所需要监听的mach port集合,用于唤醒runloop(__CFPortSet 实际上是unsigned int 类型)
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers); // 通知 即将处理 Timers
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources); // 通知 即将处理 sources
// 处理提交到runloop的blocks
__CFRunLoopDoBlocks(rl, rlm);
// 处理 source0
Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
if (sourceHandledThisLoop) {
__CFRunLoopDoBlocks(rl, rlm); // 处理提交的runloop的block
}
// 如果有source1被signaled,则不休眠,直接跳到handle_msg来处理source1
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
goto handle_msg;
}
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting); // 通知observer before waiting
// ****** 开始休眠 ******
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy); // 调用__CFRunLoopServiceMachPort, 监听waitSet所指定的mach port端口集合, 如果没有port message,进入 mach_msg_trap, RunLoop休眠,直到收到port message或者超时
// ****** 休眠结束 ******
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting); // 通知observer, runloop醒了
handle_msg:; // 处理事件
// 根据唤醒RunLoop的livePort值,来进行对应逻辑处理
if (MACH_PORT_NULL == livePort) { // MACH_PORT_NULL: 可能是休眠超时,啥都不做
CFRUNLOOP_WAKEUP_FOR_NOTHING();
// handle nothing
} else if (livePort == rl->_wakeUpPort) { // rl->_wakeUpPort: 被其他线程或进程唤醒,啥都不做
CFRUNLOOP_WAKEUP_FOR_WAKEUP();
// do nothing on Mac OS
} else if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) // rlm->_timerPort: 处理nstimer 消息
if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
// Re-arm the next timer
__CFArmNextTimerInMode(rlm, rl);
}
}else if (livePort == dispatchPort) // dispatchPort:处理分发到main queue上的事件
{
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
}else { // 其余的,肯定是各种source1 事件
__CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply)
if (NULL != reply) { // 如果有需要回复soruce1的消息,则回复
(void)mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
CFAllocatorDeallocate(kCFAllocatorSystemDefault, reply);
}
}
// ****** 到这里就结束了对livePort的处理 ******
__CFRunLoopDoBlocks(rl, rlm); // 处理提交到runloop的blocks
// 检查runloop是否需要退出
if (sourceHandledThisLoop && stopAfterHandle) { // case1. 指定了仅处理一个source 退出kCFRunLoopRunHandledSource
retVal = kCFRunLoopRunHandledSource;
} else if (timeout_context->termTSR < mach_absolute_time()) { // case2. RunLoop 超时
retVal = kCFRunLoopRunTimedOut;
} else if (__CFRunLoopIsStopped(rl)) { // case3. RunLoop 被终止
__CFRunLoopUnsetStopped(rl);
retVal = kCFRunLoopRunStopped;
} else if (rlm->_stopped) { // case4. RunLoop Mode 被终止
rlm->_stopped = false;
retVal = kCFRunLoopRunStopped;
} else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) { // case5. RunLoop Mode里面没有任何要被处理的事件了(没有source0,source1, timer,以及提交到当前runloop mode的block)
retVal = kCFRunLoopRunFinished;
}
}while(0 == retVal);
// runloop循环结束,返回退出原因
return retVal;
}
总结一下:
RunLoop仅会对当前mode下的source,timer和observer进行处理
RunLoop在各个状态下会对observer发送相应通知。通知顺序是:
进入RunLoop循环前: (1)kCFRunLoopEntry
进入循环((2)–(4)反复循环):
(2)kCFRunLoopBeforeTimers -> (3)kCFRunLoopBeforeSources -> (4)kCFRunLoopBeforeWaiting -> (5)kCFRunLoopAfterWaiting
退出循环:(6)kCFRunLoopExit
RunLoop通过调用__CFRunLoopServiceMachPort,通过Mach Port监听Ports来实现休眠(陷入mach_msg_trap状态)。可以唤醒RunLoop的事件包括:
(1) Mach Port监听超时
(2)被其他线程\进程唤醒
(3)有Timer事件需要执行
(4)有提交到main queue上的block(当前RunLoop是main RunLoop时才有这种情况)
(5)被source1唤醒
RunLoop退出的可能原因有:
(1)RunLoop超时
(2)处理完事件就退出stopAfterHandle
(3)RunLoop被终止
(4)RunLoop Mode被终止
(5)RunLoop Mode里面source0, source1, timer队列都空了,没啥要处理了
当APP没有任何事件处理时,通过xcode的APP暂停键,可以看到APP处于mach_msg_trap,等待消息状态
苹果用RunLoop实现的功能
事件响应
main RunLoop会在所有mode下注册source0事件源
<CFRunLoopSource 0x6040001729c0 [0x1029cebb0]>{signalled = No, valid = Yes, order = -1, context = <CFRunLoopSource context>{version = 0, info = 0x60400014e0d0, callout = __handleEventQueue (0x1034e5dcc)}}
他们有一个共同的回调函数__handleEventQueue,但由于他们是source0的事件源,无法自己唤醒,所以需要一个source1去唤醒,这个source1就被一个名字叫做com.apple.uikit.eventfetch-thread的子线程的runloop注册,它的回调是__IOHIDEventSystemClientQueueCallback。因为source1是通过端口监听,可以被唤醒的。所以在soure1被唤醒时,会将manRunLoop的source0置为signalled=YES的状态,同时唤醒mainRunLoop。manLoop则调用__handleEventQueue进行事件处理。
手势识别
iOS的手势识别也依赖于RunLoop。UIKit会向mainRunLoop注册一个observer,该observer监听mainRunLoop的kCFRunLoopBeforeWaing事件,每当main RunLoop即将休眠时,该observer被处罚,同时调用函数_UIGestureRecognizerUpdateObserver。_UIGestureRecognizerUpdateObserver会检测当前需要被更新的状态的recognizer。
如果有手势处罚,如果有手势被触发,在_UIGestureRecognizerUpdateObserver回调中会借助UIKit一个内部类UIGestureEnvironment
来进行一系列处理,会向APP的event queue投递一个gesture event,内部会调用__handleEventQueueInternal处理该gesture event,最终回到我们自己写的gesture回调中。
界面刷新
当我们需要界面刷新的,如UIView/CALayer调用setNeedsLayout/setNeedsDisplay,或更新了UIView的frame或UI层次。其实系统并不是立刻去刷新界面的,而是先提交UI刷新请求,等到下次一mainRunLoop循环时,集中处理,而这是通过监听mainRunLoop的beforeWaiting和Exit通知实现的。
该observer的回调是
_ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv
其内部会调用
CA::Transaction::observer_callback // 位于QuartzCore 中
该函数中,会将所有的界面刷新请求提交,刷新界面,以及调用相关回调。