准备
一、RunLoop 应用
在runloop
官方文档中有下面这张经典的图:
怎么理解这张图呢,下面通过示例进行分析,ViewController
中添加下面代码:
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"二百五");
}];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(gotNotification:) name:@"helloMyNotification" object:nil];
[self performSelector:@selector(fire) withObject:nil afterDelay:1.0];
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"hello word");
});
void (^block)(void) = ^{
NSLog(@"123");
};
block();
}
- (void)fire {
NSLog(@"performSeletor");
}
- (void)gotNotification:(NSNotification *)noti {
NSLog(@"gotNotification = %@",noti);
}
#pragma mark - 触摸事件
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSLog(@"来了,老弟!!!");
[[NSNotificationCenter defaultCenter] postNotificationName:@"helloMyNotification" object:@"ssl"];
}
针对NSTimer
进行bt
测试:
- 可以看到
timer
是受runloop
控制的。 - 我们还能看到有个
__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__
的调用。
再看下NSNotification
的bt
都打印了什么:
- 这里有个
__CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__
的调用。
剩下的几种情况可自行测试,我这里经过测试可以得到下面的对应关系:
block
应用:__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__
。- 调用
timer
:__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__
。 - 响应
source0
:__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
。 - 响应
source1
:__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__
。 GCD
主队列:__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__
。observer
源:__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__
。
二、RunLoop 作用
- 保持程序的持续运行。
- 处理
APP
中的各种事件(触摸、定时器、performSelector
等)。 - 节省
cpu
资源、提供程序的性能:该做事就做事,该休息就休息。
三、RunLoop 数据结构
我们知道RunLoop
的底层是CFRunLoop
,接下来我们打开CFRunLoop
的源码,先从CFRunLoopRun
开始分析:
void CFRunLoopRun(void) { /* DOES CALLOUT */
int32_t result;
do {
result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
CHECK_FOR_FORK();
} while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}
可以看到函数内部是一个do while
循环,继续查看CFRunLoopGetCurrent()
函数:
CFRunLoopRef CFRunLoopGetCurrent(void) {
CHECK_FOR_FORK();
CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
if (rl) return rl;
return _CFRunLoopGet0(pthread_self());
}
3.1 RunLoop 和 线程
查看_CFRunLoopGet0
函数:
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());
// 进行绑定 dict[@"pthread_main_thread_np"] = mainLoop
CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
CFRelease(dict);
}
CFRelease(mainLoop);
__CFLock(&loopsLock);
}
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
__CFUnlock(&loopsLock);
// ******如果不是主线程,进行类似操作将线程和创建的CFRunLoopRef进行绑定
if (!loop) {
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
__CFLock(&loopsLock);
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
if (!loop) {
CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
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())) {
_CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL);
if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) {
_CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop);
}
}
return loop;
}
- 函数主要做的就是创建
RunLoop
,通过CFMutableDictionaryRef
将RunLoop
和线程
进行绑定,它们是一一对应关系:
3.2 CFRunLoopRef
创建RunLoop
是使用的__CFRunLoopCreate
函数,查看函数实现:
static CFRunLoopRef __CFRunLoopCreate(pthread_t t) {
CFRunLoopRef loop = NULL;
CFRunLoopModeRef rlm;
uint32_t size = sizeof(struct __CFRunLoop) - sizeof(CFRuntimeBase);
loop = (CFRunLoopRef)_CFRuntimeCreateInstance(kCFAllocatorSystemDefault, CFRunLoopGetTypeID(), size, NULL);
if (NULL == loop) {
return NULL;
}
(void)__CFRunLoopPushPerRunData(loop);
__CFRunLoopLockInit(&loop->_lock);
loop->_wakeUpPort = __CFPortAllocate();
if (CFPORT_NULL == loop->_wakeUpPort) HALT;
__CFRunLoopSetIgnoreWakeUps(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;
loop->_winthread = 0;
rlm = __CFRunLoopFindMode(loop, kCFRunLoopDefaultMode, true);
if (NULL != rlm) __CFRunLoopModeUnlock(rlm);
return loop;
}
- 可以看到
RunLoop
其实就是CFRunLoopModeRef
,函数中对它做了一些初始化的操作。
CFRunLoopRef
是结构体指针,查看__CFRunLoop
结构体:
typedef struct __CFRunLoop * CFRunLoopRef;
// CFRunLoop.c
struct __CFRunLoop {
pthread_t _pthread;
CFMutableSetRef _commonModes;
CFMutableSetRef _commonModeItems;
CFRunLoopModeRef _currentMode;
CFMutableSetRef _modes;
...
};
3.3 CFRunLoopModeRef
查看CFRunLoopModeRef
:
typedef struct __CFRunLoopMode *CFRunLoopModeRef;
struct __CFRunLoopMode {
CFStringRef _name;
CFMutableSetRef _sources0;
CFMutableSetRef _sources1;
CFMutableArrayRef _observers;
CFMutableArrayRef _timers;
...
};
CFRunLoopModeRef
代表RunLoop
的运行模式;- 一个
RunLoop
包含若干个 Mode,每个 Mode 又包含若干个Source0
/Source1
/Timer
/Observer
; RunLoop
启动时只能选择其中一个 Mode,作为 currentMode;- 如果需要切换 Mode,只能退出当前 Loop,再重新选择一个 Mode 进入,切换模式不会导致程序退出;
- 不同 Mode 中的
Source0
/Source1
/Timer
/Observer
能分隔开来,互不影响; - 如果 Mode 里没有任何
Source0
/Source1
/Timer
/Observer
,RunLoop
会立马退出。 __CFRunLoopMode
结构图如下:
3.4 RunLoop 的常见模式
ModeName | 描述 |
---|---|
NSDefaultRunLoopMode / NSDefaultRunLoopMode | 默认模式 |
UITrackingRunLoopMode | 界面追踪模式,用于 ScrollView 追踪触摸滑动, 保证界面滑动时不受其他 Mode 影响; |
NSRunLoopCommonModes / KCFRunLoopCommonModes | 该模式不是实际存在的一种模式,它只是一个特殊的标记,是同步Source0 /Source1 /Timer /Observer 到多个 Mode 中的技术方案。被标记为通用模式的Source0 /Source1 /Timer /Observer 都会存放到 _commonModeItems 集合中,会同步这些Source0 /Source1 /Timer /Observer 到多个 Mode 中。 |
3.5 CFRunLoopModeRef 这样设计有什么好处?Runloop为什么会有多个 Mode?
- Mode 做到了屏蔽的效果,当
RunLoop
运行在 Mode1 下面的时候,是处理不了 Mode2 的事件的; - 比如
NSDefaultRunLoopMode
默认模式和UITrackingRunLoopMode
滚动模式,滚动屏幕的时候就会切换到滚动模式,就不用去处理默认模式下的事件了,保证了UITableView
等的滚动顺畅。
3.6 CFRunLoopSourceRef
Source
有两个版本:Source0
和Source1
。
Source0
只包含了一个回调(函数指针),它并不能主动触发事件。Source1
包含了一个 mach_port 和一个回调(函数指针),被用于通过内核和其他线程相互发送消息。这种 Source 能主动唤醒 RunLoop 的线程。- 以下
__CFRunLoopSource
中的共用体union
中的version0
和version1
就分别对应Source0
和Source1
。
// CFRunLoop.h
typedef struct __CFRunLoopSource * CFRunLoopSourceRef;
// CFRunLoop.m
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;
};
Source0 和 Source1 的区别:
Sources | 区别 |
---|---|
Source0 | 需要手动唤醒线程:添加Source0 到RunLoop 并不会主动唤醒线程,需要手动唤醒)1. 触摸事件处理 2. performSelector:onThread: |
Source1 | 具备唤醒线程的能力 1. 基于 Port 的线程间通信 2. 系统事件捕捉:系统事件捕捉是由 Source1 来处理,然后再交给Source0 处理 |
3.7 CFRunLoopTimerRef
CFRunloopTimer
和NSTimer
是 toll-free bridged 的,可以相互转换;- 其包含一个时间长度和一个回调(函数指针)。当其加入到
RunLoop
时,RunLoop
会注册对应的时间点,当时间点到时,RunLoop
会被唤醒以执行那个回调。
// CFRunLoop.h
typedef struct CF_BRIDGED_MUTABLE_TYPE(NSTimer) __CFRunLoopTimer * CFRunLoopTimerRef;
// CFRunLoop.c
struct __CFRunLoopTimer {
CFRuntimeBase _base;
uint16_t _bits;
pthread_mutex_t _lock;
CFRunLoopRef _runLoop; // 添加该 timer 的 RunLoop
CFMutableSetRef _rlModes; // 所有包含该 timer 的 modeName
CFAbsoluteTime _nextFireDate;
CFTimeInterval _interval; /* immutable 理想时间间隔 */
CFTimeInterval _tolerance; /* mutable 时间偏差 */
uint64_t _fireTSR; /* TSR units */
CFIndex _order; /* immutable */
CFRunLoopTimerCallBack _callout; /* immutable 回调入口 */
CFRunLoopTimerContext _context; /* immutable, except invalidation */
};
3.8 CFRunLoopObserverRef
每个Observer
都包含了一个回调(函数指针),当RunLoop
的状态发生变化时,观察者就能通过回调接受到这个变化。下面是可以监听的6
种活动状态:
/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), // 即将进入 RunLoop
kCFRunLoopBeforeTimers = (1UL << 1), // 即将处理 Timers
kCFRunLoopBeforeSources = (1UL << 2), // 即将处理 Sources
kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠
kCFRunLoopAfterWaiting = (1UL << 6), // 刚从休眠中唤醒
kCFRunLoopExit = (1UL << 7), // 即将退出 RunLoop
kCFRunLoopAllActivities = 0x0FFFFFFFU // 表示以上所有状态
};
- 上面的
Source
/Timer
/Observer
被统称为mode item
,一个item
可以被同时加入多个mode
。如果一个mode
中一个item
都没有,则RunLoop
会直接退出,不进入循环。
3.9 RunLoop 整体结构
通过上面的分析可以得到RunLoop
的整体结构图如下:
四、RunLoop 的原理
提到RunLoop
的原理,下面这幅图相信大家都见过:
下面通过源码分析来理解上面的图,先查看CFRunLoopRun
函数:
void CFRunLoopRun(void) { /* DOES CALLOUT */
int32_t result;
do {
result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
CHECK_FOR_FORK();
} while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}
while
条件判断result
的状态不是kCFRunLoopRunStopped
和kCFRunLoopRunFinished
的时候,循环体就不会结束。result
是CFRunLoopRunSpecific
函数的返回结果。1.0e10
是科学计数法,大小为1*10^10
,用来做超时判断。
4.1 CFRunLoopRunSpecific
那么来查看CFRunLoopRunSpecific
函数:
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);
/// 1. 通知 Observers: RunLoop 即将进入 loop。
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
/// 核心函数,进入loop
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
/// 10. 通知 Observers: RunLoop 即将退出。
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
return result;
}
- 以上代码可以看到
第1步
和第10步
,也就是即将进入
和即将退出
的时候都会对Observers
进行通知,其他步骤在__CFRunLoopRun
函数中进行。
4.2 __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) {
int32_t retVal = 0;
do { // itmes do
/// 2. 通知 Observers: RunLoop 即将触发 Timer 回调。
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
/// 3. 通知 Observers: RunLoop 即将触发 Source0 (非port) 回调。
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources)
/// 执行被加入的block
__CFRunLoopDoBlocks(rl, rlm);
/// 4. RunLoop 触发 Source0 (非port) 回调。
Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
/// 处理sources0返回为YES
if (sourceHandledThisLoop) {
/// 执行被加入的block
__CFRunLoopDoBlocks(rl, rlm);
}
/// 5. 如果有 Source1 (基于port) 处于 ready 状态,直接处理这个 Source1 然后跳转去处理消息。
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
/// 如果接收到了消息的话,前往第9步开始处理消息
goto handle_msg;
}
/// 6.通知 Observers: RunLoop 的线程即将进入休眠(sleep)。
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
__CFRunLoopSetSleeping(rl);
/// 7. 接收waitSet端口的消息
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
/// 7. 等待接受 mach_port 的消息。线程将进入休眠, 直到被下面某一个事件唤醒。
/// • 一个基于 port 的Source 的事件。
/// • 一个 Timer 到时间了
/// • RunLoop 自身的超时时间到了
/// • 被其他什么调用者手动唤醒
// 取消runloop的休眠状态
__CFRunLoopUnsetSleeping(rl);
/// 8. 通知 Observers: RunLoop 的线程刚刚被唤醒了。
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
handle_msg:
if (被Timer唤醒) {
/// 9.1 如果一个 Timer 到时间了,触发这个Timer的回调。
__CFRunLoopDoTimers(rl, rlm, mach_absolute_time());
} else if (被GCD唤醒) {
/// 9.2 如果有dispatch到main_queue的block,执行block
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
} else if (被Source1唤醒) {
/// 9.3 如果一个 Source1 (基于port) 发出事件了,处理这个事件
__CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply)
}
/// 执行加入到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);
return retVal;
}
- 这是
runloop
的核心函数,从第2步
和第9步
是事务的执行以及休眠等操作。 - 如果没超时、
mode
里没空、loop
也没被停止,loop
会继续执行。否则循环退出并返回retVal
,retVal
有kCFRunLoopRunHandledSource
、kCFRunLoopRunTimedOut
、kCFRunLoopRunStopped
、kCFRunLoopRunFinished
四种状态。
五、RunLoop 是如何处理事务的
我们以NSTimer
为例来分析RunLoop
是如何处理事务的,事务的处理分为添加和执行,下面以这两方面来进行分析。
5.1 timer 的添加
CFRunLoopAddTimer()
函数中会判断传入的modeName
模式名称是不是
kCFRunLoopCommonModes
通用模式,是的话就会将timer
添加到RunLoop
的 _commonModeItems 集合中,并同步该timer
到 _commonModes 里的所有模式中,这样无论在默认模式还是界面追踪模式下NSTimer
都可以执行。
void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeName) {
CHECK_FOR_FORK();
if (__CFRunLoopIsDeallocating(rl)) return;
if (!__CFIsValid(rlt) || (NULL != rlt->_runLoop && rlt->_runLoop != rl)) return;
__CFRunLoopLock(rl);
if (modeName == kCFRunLoopCommonModes) { // 判断 modeName 是不是 kCFRunLoopCommonModes
CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
if (NULL == rl->_commonModeItems) { // 懒加载,判断 _commonModeItems 是否为空,是的话创建
rl->_commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
}
CFSetAddValue(rl->_commonModeItems, rlt); // 将 timer 添加到 _commonModeItems 中
if (NULL != set) {
CFTypeRef context[2] = {rl, rlt}; // 将 timer 和 RunLoop 封装到 context 中
/* add new item to all common-modes */
// 遍历 commonModes,将 timer 添加到 commonModes 的所有模式下
CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
CFRelease(set);
}
......
}
}
static void __CFRunLoopAddItemsToCommonMode(const void *value, void *ctx) {
CFTypeRef item = (CFTypeRef)value;
CFRunLoopRef rl = (CFRunLoopRef)(((CFTypeRef *)ctx)[0]);
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);
}
}
5.2 timer 的执行
根据上面分析我们知道loop
的9.1
步时会调用__CFRunLoopDoTimers
函数,会遍历timers
,最终调用__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__
回调函数,正是我们最开始bt
调试时打印出来的。
static Boolean __CFRunLoopDoTimers(CFRunLoopRef rl, CFRunLoopModeRef rlm, uint64_t limitTSR) {
Boolean timerHandled = false;
CFMutableArrayRef timers = rlm->_timers;
for (CFIndex idx = 0; idx < CFArrayGetCount(timers); idx++) {
CFRunLoopTimerRef rlt = CFArrayGetValueAtIndex(timers, idx);
Boolean did = __CFRunLoopDoTimer(rl, rlm, rlt);
timerHandled = timerHandled || did;
}
return timerHandled;
}
static Boolean __CFRunLoopDoTimer(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopTimerRef rlt) {
if (__CFIsValid(rlt) && rlt->_fireTSR <= mach_absolute_time() && !__CFRunLoopTimerIsFiring(rlt) && rlt->_runLoop == rl) {
__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(rlt->_callout, rlt, context_info);
}
}