RunLoop简介
RunLoop
,就是一个运行循环,通过一个内部的运行循环(Event Loop
)对事件或者消息管理的一个对象
他是通过一个 do while
循环来保持一致运行的(main
函数不会退出的原因),然后通过sleep
机制来降低CPU
的占用问题
特点:
- 有消息要处理时,唤醒
RunLoop
来处理事件 - 没有消息时,进行休眠,避免占用太多资源
- 和线程是一一对应的
void CFRunLoopRun(void) { /* DOES CALLOUT */
int32_t result;
do {
result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
CHECK_FOR_FORK();
} while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}
RunLoop 和线程的关系
苹果不允许我们直接创建RunLoop
,提供了两个获取的函数:CFRunLoopGetMain
和CFRunLoopGetCurrent
,
RunLoop
和线程一一对应
只能操作当前线程的RunLoop
,不能操作其他线程
RunLoop
在第一次获取时创建([NSRunLoop current]
),在线程结束时销毁
源码解析
CFRunLoopRef CFRunLoopGetMain(void) {
CHECK_FOR_FORK();
static CFRunLoopRef __main = NULL; // no retain needed
if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np()); // no CAS needed
return __main;
}
//__CFRunLoops 是一个字典
static CFMutableDictionaryRef __CFRunLoops = NULL;
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);
CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
// 进行绑定 dict[@"pthread_main_thread_np"] = mainLoop
CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
//将dict保存在__CFRunLoops中,释放dict
if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
CFRelease(dict);
}
CFRelease(mainLoop);
__CFLock(&loopsLock);
}
//获取loop
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
__CFUnlock(&loopsLock);
//如果没有获取到创建新的
if (!loop) {
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
__CFLock(&loopsLock);
//获取loop
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
//如果没有获取到创建新的
if (!loop) {
//进行绑定
CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
loop = newLoop;
}
__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;
}
- 如果不存在
__CFRunLoops
,创建要一个CFMutableDictionaryRef
- 根据当前线程创建一个
CFRunLoopRef
- 通过kvc将线程做为key,loop作为value存入字典,简历对应关系
RunLoop结构,如何创建
刚才研究RunLoop
和线程的关系,可以看出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);
.....
return loop;
}
struct __CFRunLoop {
CFRuntimeBase _base;
pthread_mutex_t _lock; /* locked for accessing mode list */
__CFPort _wakeUpPort; // used for CFRunLoopWakeUp
Boolean _unused;
volatile _per_run_data *_perRunData; // reset for runs of the run loop
pthread_t _pthread;
uint32_t _winthread;
CFMutableSetRef _commonModes;
CFMutableSetRef _commonModeItems;
CFRunLoopModeRef _currentMode;
CFMutableSetRef _modes;
struct _block_item *_blocks_head;
struct _block_item *_blocks_tail;
CFAbsoluteTime _runTime;
CFAbsoluteTime _sleepTime;
CFTypeRef _counterpart;
};
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;
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
uint64_t _timerSoftDeadline; /* TSR */
uint64_t _timerHardDeadline; /* TSR */
};
-
RunLoop
是一个CFRunLoopRef
对象.有_pthread
,_commonModes
,_commonModeItems
,等属性 -
__CFRunLoopMode
,有_sources0
,_sources1
,_observers
,_timers
等属性 -
RunLoop
跟线程是一对一绑定关系 -
RunLoop
有多个CFRunLoopMode
- 一个
CFRunLoopMode
有多个CFRunLoopSource
CFRunLoopTimer
CFRunLoopObserver
Timer,Source,Block等是如何加入到RunLoop中并且执行的
我们在使用Timer
时往往会遇到一些问题,比如timer
运行时我们滑动界面,这个时候timer就会停止运行,因为timer
的运行是依赖于RunLoop
的,创建出来默认是在defaultModel
中的,当我们滑动界面时Model
就会从defaultModel
变为UITrackingRunLoopMode
,Model
只能存在一个所以这个时候 Timer
暂停运行,这个时候我们往往会这样写
NSTimer *timer = [NSTimer timerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"fire in home -- %@",[[NSRunLoop currentRunLoop] currentMode]);
}];
[[NSRunLoop currentRunLoop] addTimer:timer forMode: UITrackingRunLoopMode];
那么[[NSRunLoop currentRunLoop] addTimer:timer forMode: UITrackingRunLoopMode];
到底干了什么呢
我们在Timer的block中打个断点,然后看下他的调用堆栈,看下他的执行流程
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
* frame #0: 0x0000000106c978bc 03-Runloop`__27-[ViewController timerDemo]_block_invoke(.block_descriptor=0x0000000106c9a0f0, timer=0x000060000002c780) at ViewController.m:50:38
frame #1: 0x00007fff20871584 Foundation`__NSFireTimer + 67
frame #2: 0x00007fff203a9112 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 20
frame #3: 0x00007fff203a8be5 CoreFoundation`__CFRunLoopDoTimer + 926
frame #4: 0x00007fff203a8198 CoreFoundation`__CFRunLoopDoTimers + 265
frame #5: 0x00007fff203a2826 CoreFoundation`__CFRunLoopRun + 1949
frame #6: 0x00007fff203a1b9e CoreFoundation`CFRunLoopRunSpecific + 567
frame #7: 0x00007fff2b773db3 GraphicsServices`GSEventRunModal + 139
frame #8: 0x00007fff24660af3 UIKitCore`-[UIApplication _run] + 912
frame #9: 0x00007fff24665a04 UIKitCore`UIApplicationMain + 101
frame #10: 0x0000000106c97d30 03-Runloop`main(argc=1, argv=0x00007ffee8f67be0) at main.m:15:16
frame #11: 0x00007fff20257415 libdyld.dylib`start + 1
(lldb)
可以看出调用流程是这样的:
CFRunLoopRunSpecific
->__CFRunLoopRun
->__CFRunLoopDoTimers
->__CFRunLoopDoTimer
-> __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__
CFRunLoopRunSpecific
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */
CHECK_FOR_FORK();
if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
return result;
}
-
__CFRunLoopDoObservers
告诉RunLoop
要开始run
了 __CFRunLoopRun
-
__CFRunLoopDoObservers
告诉RunLoop
退出了
__CFRunLoopRun
static int32_t __CFRunLoopRun__CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
// ....
do {
//通知observers即将处理timer事件
if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
//通知observers即将处理Sources事件
if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
//处理block
__CFRunLoopDoBlocks(rl, rlm);
//处理source0
Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
//处理sources0返回为YES
if (sourceHandledThisLoop) {
//处理block
__CFRunLoopDoBlocks(rl, rlm);
}
/// 判断有无端口消息(Source1)
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
/// 处理消息
goto handle_msg;
}
/// 通知 Observers: 即将进入休眠
if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
__CFRunLoopSetSleeping(rl);
///等待被唤醒
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
/// 通知 Observers: 被唤醒,结束休眠 (系统级别的通知实现)
if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
...
handle_msg:
if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
///如果被timer唤醒
CFRUNLOOP_WAKEUP_FOR_TIMER();
__CFRunLoopDoTimers(rl, rlm, mach_absolute_time()
}else if (livePort == dispatchPort) {
///如果被GCD唤醒
CFRUNLOOP_WAKEUP_FOR_DISPATCH();
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
}else {
//如果被source1处理
CFRUNLOOP_WAKEUP_FOR_SOURCE();
__CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply)
}
}while (0 == retVal);
__CFRunLoopDoTimers
// rl and rlm are locked on entry and exit
static Boolean __CFRunLoopDoTimers(CFRunLoopRef rl, CFRunLoopModeRef rlm, uint64_t limitTSR) { /* DOES CALLOUT */
Boolean timerHandled = false;
CFMutableArrayRef timers = NULL;
for (CFIndex idx = 0, cnt = rlm->_timers ? CFArrayGetCount(rlm->_timers) : 0; idx < cnt; idx++) {
CFRunLoopTimerRef rlt = (CFRunLoopTimerRef)CFArrayGetValueAtIndex(rlm->_timers, idx);
if (__CFIsValid(rlt) && !__CFRunLoopTimerIsFiring(rlt)) {
if (rlt->_fireTSR <= limitTSR) {
if (!timers) timers = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
CFArrayAppendValue(timers, rlt);
}
}
}
for (CFIndex idx = 0, cnt = timers ? CFArrayGetCount(timers) : 0; idx < cnt; idx++) {
CFRunLoopTimerRef rlt = (CFRunLoopTimerRef)CFArrayGetValueAtIndex(timers, idx);
Boolean did = __CFRunLoopDoTimer(rl, rlm, rlt);
timerHandled = timerHandled || did;
}
if (timers) CFRelease(timers);
return timerHandled;
}
- 通过循环遍历,然后判断
__CFIsValid(rlt) && !__CFRunLoopTimerIsFiring(rlt)
-
CFArrayAppendValue(timers, rlt)
将timer
添加到timers
中 - 然后进行遍历,调用
__CFRunLoopDoTimer
__CFRunLoopDoTimer
static Boolean __CFRunLoopDoTimer(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopTimerRef rlt) {
__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(rlt->_callout, rlt, context_info);
}
-
__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__
进行回调,行程闭环
依次类推,Source
,Observer
,Block
原理大同小异
RunLoop 主要处理item 处理负责休眠的事物
如何创建一个常驻线程
- 为当前线程创建一个
RunLoop
- 像当前
RunLoop
中添加一个port
或者source
来维持RunLoop
事件循环 - 启动
RunLoop
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[runLoop run];
总结
RunLoop原理:
- 通知observer即将进入loop
- 通知Observer,即将处理timer
- 通知Observer,即将处理Source0
- 处理Source0
- 如果有Source1,跳至第九步
- 通知Observer,线程即将休眠
- 休眠等待唤醒
- 通知Observer,线程被唤醒
- 处理唤醒时收到的信息,之后跳回2
- 通知Observer,即将退出loop
RunLoop的item (6大事件)
- 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__
source0和source1 的区别
source0
: 用户点击等事件,按钮点击,触摸等事件,无法唤醒RunLoop
source1
:系统端口信息,基于port
,可以直接唤醒loop
** CFRunLoopModem五种模式**
kCFRunLoopDefaultMode
:默认模式,主线程是在这个运行模式下运行
UITrackingRunLoopMode
:跟踪用户交互事件(用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他Mode影响)
UIInitializationRunLoopMode
:在刚启动App时第进入的第一个 Mode,启动完成后就不再使用
GSEventReceiveRunLoopMode
:接受系统内部事件,通常用不到
kCFRunLoopCommonModes
:伪模式,不是一种真正的运行模式,是同步Source/Timer/Observer
到多个Mode
中的一种解决方案
kCFRunLoopEntry = (1UL << 0),
kCFRunLoopBeforeTimers = (1UL << 1),
kCFRunLoopBeforeSources = (1UL << 2),
kCFRunLoopBeforeWaiting = (1UL << 5),
kCFRunLoopAfterWaiting = (1UL << 6),
kCFRunLoopExit = (1UL << 7),
kCFRunLoopAllActivities = 0x0FFFFFFFU