RunLoop
顾名思义就是运行循环
,就是在程序运行过程中循环做一些事情。
应用范围:
1、定时器(Timer)、PerformSelector
2、GCD Async Main Queue
3、事件响应、手势识别、界面刷新
4、网络请求
5、AutoreleasePool
这里里面都用到了runloop。
RunLoop的基本作用:1.保持程序的持续运行 2.处理App中的各种事件(比如触摸事件、定时器事件等)3.节约CPU资源,提高程序性能:该做事时做事,该休息时休息
RunLoop对象
ios中有2套API来访问和使用RunLoop
- Foundation:NSRunLoop
//获取当前线程的runloop
NSRunLoop *runloop1 = [NSRunLoop currentRunLoop];
//获取主线程的runloop
NSRunLoop *runloop2 = [NSRunLoop mainRunLoop];
- Core Foundation:CFRunLoopRef
//获取当前线程的runloop
CFRunLoopRef runloop3 = CFRunLoopGetCurrent();
//获取主线程的runloop
CFRunLoopRef runloop4 = CFRunLoopGetMain();
//打印上面4个对象的内存地址
NSLog(@"%p %p %p %p",runloop1,runloop2,runloop3,runloop4);
我们惊讶的发现2套api获取的主线程的runloop竟然不是一个对象。这是因为NSRunLoop对象对CFRunLoopRef对象进行了一层包装,本质上还是CFRunLoopRef对象。
CFRunLoopRef
Core Foundation中关于RunLoop的5个类
CFRunLoopRef
CFRunLoopModeRef
CFRunLoopSourceRef
CFRunLoopTimerRef
CFRunLoopObserverRef
typedef struct __CFRunLoop * CFRunLoopRef;
struct __CFRunLoop {
CFRuntimeBase _base;
pthread_t _pthread; //对应的线程
CFMutableSetRef _commonModes; //Set<CFStringRef>
CFMutableSetRef _commonModeItems; //Set<Source/Observer/Timer>
CFRunLoopModeRef _currentMode; //当前的CFRunLoopModeRef对象
CFMutableSetRef _modes; //存储了一堆的CFRunLoopModeRef对象
};
typedef struct __CFRunLoopMode *CFRunLoopModeRef;
struct __CFRunLoopMode {
CFStringRef _name; //模式的名字
CFMutableSetRef _sources0; //存储CFRunLoopSourceRef对象
CFMutableSetRef _sources1; //存储CFRunLoopSourceRef对象
CFMutableArrayRef _observers; //存储CFRunLoopObserverRef对象
CFMutableArrayRef _timers; //存储CFRunLoopTimerRef对象
};
他们之间的关系如下图
CFRunLoopMode
代表RunLoop
的运行模式- 一个
RunLoop
包含若干个Mode
,每个Mode有包含若干个Source0/Source1/Timer/Observer
。 RunLoop
启动时只能选择其中的一个Mode
作为currentMode
。- 如果需要切换
Mode
,只能退出当前Loop
,再重新选择一个Mode
进入。 - 如果
Mode
中没有任何的Source0/Source1/Timer/Observer
,RunLoop
会立马退出
注意:不同mode下的
Source0/Source1/Timer/Observer
分隔开来,互不影响,提高效率
CFRunLoopModeRef
Mode有以下几种
kCFRunLoopDefaultMode 默认Mode,主线程通常在这种模式下运行
UITrackingRunLoopMode 追踪Mode,保证UIScrollView滑动顺畅不受其他Mode影响
kCFRunLoopCommonModes 占位Mode,并不是一个真的模式,只是一个标记。相当于 kCFRunLoopDefaultMode+UITrackingRunLoopMode
UIInitializationRunLoopMode App启动后的过渡Mode,启动完成不在使用
GSEventReceiveRunLoopMode Graphic相关事件Mode,通常用不到
下面两种我们基本不用 可以忽略
每种Mode
中有存储这个若干个Source0/Source1/Timer/Observer
- Source0
- 触摸事件处理
- performSelector:onThread:
- Source1
- 基于Port的线程间通信
- 系统事件捕捉
- Timers
- NSTimer
- performSelector:withObject:afterDelay:
- Observers
- 用于监听RunLoop的状态
- UI 刷新(BeforeWaiting)
- AutoRelease pool (BeforWaiting)
我们看到左侧的调用堆栈从15直接过渡了1,我们在调试台那里输入
bt
,显示出所有的调用堆栈
Source0、Timers、Observers的其他情况也可以如此证明。
通过上图还发现了一次调用了CFRunLoopRunSpecific
、__CFRunLoopRun
、__CFRunLoopDoSources0
、__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
方法。其中CFRunLoopRunSpecific
、__CFRunLoopRun
为runloop的主题函数,__CFRunLoopDoSources0
、__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
是Source0的处理函数,同理可推还有Source1/Timer/Observer的处理函数。
CFRunLoopObserverRef
我们可以给runloop添加observer来监听runloop的状态改变。 runloop有以下几种状态
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), //即将进入Loop
kCFRunLoopBeforeTimers = (1UL << 1), //即将处理Timer
kCFRunLoopBeforeSources = (1UL << 2), //即将处理Source
kCFRunLoopBeforeWaiting = (1UL << 5), //即将进入休眠
kCFRunLoopAfterWaiting = (1UL << 6), //刚从休眠中唤醒
kCFRunLoopExit = (1UL << 7), //即将退出Loop
kCFRunLoopAllActivities = 0x0FFFFFFFU //所有状态
};
给runloop添加监听
//创建监听对象
//kCFRunLoopAllActivities监听所有对象
//YES重复监听
//0优先级 NSIntegerMax:为优先级最小
//MYRunLoopObserverCallBack回调函数
//Null 这里可以为回调函数传入一些值 在回调函数的info里面获取到
CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, MYRunLoopObserverCallBack, NULL);
//添加监听对象
CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
//释放监挺对象
CFRelease(observer);
//监听函数
void MYRunLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
}
还有另外一种添加observer方式
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
//回调block 利用block写到了一起,就用不到info来传递值了
});
CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
CFRelease(observer);
RunLoop与线程
CFRunLoopRef CFRunLoopGetMain(void) {
CHECK_FOR_FORK();
static CFRunLoopRef __main = NULL;
if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np());
return __main;
}
CFRunLoopRef CFRunLoopGetCurrent(void) {
CHECK_FOR_FORK();
CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
if (rl) return rl;
return _CFRunLoopGet0(pthread_self());
}
//最后动调用了_CFRunLoopGet0并传入了一个*线程*
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
//如果是空线程那就默认主线程
if (pthread_equal(t, kNilPthreadT)) {
t = pthread_main_thread_np();
}
__CFLock(&loopsLock);
if (!__CFRunLoops) {
__CFUnlock(&loopsLock);
//创建存储所有runloop对象的字典
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
//生成并存储主线程对象的runloop
CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
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) {
//创建新的runloop
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
__CFLock(&loopsLock);
//根据线程获取对象的runloop
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
if (!loop) {
//存储runloop
CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
loop = newLoop;
}
__CFUnlock(&loopsLock);
CFRelease(newLoop);
}
return loop;
}
通过上面的源码我们可以看出
- 每条线程有且只有一个
runloop
对象 - app所有的
runloop
保存在一个全局的Dictionary里,线程为key,runloop为value - 线程刚创建时并没有
runloop
对象,runloop
会在第一次获取它的时候创建。主线程的runloop
已经自动获取(创建),子线程默认没有runloop
。 runloop
会在线程结束时销毁。
RunLoop执行过程
//seconds时间后在rl循环的modeName模式下执行 returnAfterSourceHandled标示处理Source后是不是结束loop
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */
CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
//模式不存在或者模式中的source0、source1、timer、observer都不存在 返回kCFRunLoopRunFinished结束loop
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);
//取出loop的mode保存为previousMode
CFRunLoopModeRef previousMode = rl->_currentMode;
//修改loop的mode为传进来的mode
rl->_currentMode = currentMode;
int32_t result = kCFRunLoopRunFinished;
//通知observer进入某种mode的loop
if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
//具体要做的事情
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
//通知observer退出某种mode的loop
if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
//当传进来的mode运行结束后,mode为原来保存的那个
rl->_currentMode = previousMode;
return result;
}
/*
rl 运行的runloop
rlm 运行的mode
seconds 超时时间
stopAfterHandle ture:loop处理完事件就退出 false:一直运行到超时或者被手动退出
previousMode runloop中上次的mode
*/
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
int32_t retVal = 0;
do {
//通知observer即将处理timer
if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
//通知observer即将处理Source
if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
//处理block
__CFRunLoopDoBlocks(rl, rlm);
//处理Source0
Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
if (sourceHandledThisLoop) {
//处理block
__CFRunLoopDoBlocks(rl, rlm);
}
msg = (mach_msg_header_t *)msg_buffer;
//判断有无Source1
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
//有Source1去处理,跳转到handle_msg
goto handle_msg;
}
//通知Observer即将休眠
if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
//休眠
__CFRunLoopSetSleeping(rl);
CFAbsoluteTime sleepStart = poll ? 0.0 : CFAbsoluteTimeGetCurrent();
// 调用 mach_msg 等待接受 mach_port 的消息。线程将进入休眠, 直到被下面某一个事件唤醒。
// - 一个基于 port 的Source1 的事件。
// - 一个 Timer 到时间了
// - RunLoop 启动时设置的最大超时时间到了
// - 被手动唤醒
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort) {
mach_msg(msg, MACH_RCV_MSG, port); // thread wait for receive msg
}
//休眠结束
__CFRunLoopUnsetSleeping(rl);
//通知observer结束休眠
if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
//有Source1去处理,跳转到这里
handle_msg:;
if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {//被timer唤醒
CFRUNLOOP_WAKEUP_FOR_TIMER();
if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
//执行timer
__CFArmNextTimerInMode(rlm, rl);
}
}
else if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) {//被timer唤醒
CFRUNLOOP_WAKEUP_FOR_TIMER();
if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
//执行timer
__CFArmNextTimerInMode(rlm, rl);
}
}
else if (livePort == dispatchPort) { //被GCD唤醒
//处理GCD Async To Main Queue
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
} else {//被source1唤醒
CFRUNLOOP_WAKEUP_FOR_SOURCE();
voucher_t previousVoucher = _CFSetTSD(__CFTSDKeyMachMessageHasVoucher, (void *)voucherCopy, os_release);
CFRunLoopSourceRef rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort);
if (rls) {
mach_msg_header_t *reply = NULL;
//处理Source1
sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop;
}
}
if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);
//处理Block
__CFRunLoopDoBlocks(rl, rlm);
//设置返回值retVal
if (sourceHandledThisLoop && stopAfterHandle) {
////如果处理事件完毕,启动Runloop时设置参数为一次性执行,设置while参数退出Runloop
retVal = kCFRunLoopRunHandledSource;
} else if (timeout_context->termTSR < mach_absolute_time()) {
//启动runloop时设置的最大运行时间到了,设置while参数退出Runloop
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)) {
//runloop中的source0/source1/timer/observer都没有了,退出runloop
retVal = kCFRunLoopRunFinished;
}
} while (0 == retVal);
return retVal;
}
大体流程如下图:
__CFRunLoopRun
最终会调用后面的函数来工作。