#概念
RunLoop
是事件接收
和分发机制
的一个实现,是线程相关的基础框架的一部分,一个RunLoop就是一个事件处理的循环,用来不停的调度工作以及处理输入事件。
RunLoop
本质是一个do-while
循环,没事做就休息,来活了就干活。与普通的while循环
是有区别的,普通的while循环
会导致CPU进入忙等待状态
,即一直消耗cpu
,而RunLoop
则不会,RunLoop
是一种闲等待
,即RunLoop
具备休眠功能
。
#####RunLoop的作用
保持程序的持续运行处理APP中的各种事件(触摸、定时器、performSelector)节省cpu资源、提供程序的性能:该做事就做事,该休息就休息RunLoop源码的下载地址
#RunLoop和线程的关系
首先,iOS 开发中能遇到两个线程对象:pthread_t
和NSThread
,两者是一一对应的,可以通过pthread_main_thread_np()
或 [NSThread mainThread]
来获取主线程;也可以通过 pthread_self()
或[NSThread currentThread]
来获取当前线程。CFRunLoop
是基于pthread
来管理的。
苹果不允许直接创建 RunLoop
,它只提供了两个自动获取的函数:CFRunLoopGetMain()
和 CFRunLoopGetCurrent()
。
// 主运行循环
CFRunLoopRef mainRunloop = CFRunLoopGetMain();
// 当前运行循环
CFRunLoopRef currentRunloop = CFRunLoopGetCurrent();
/********** CFRunLoopGetMain ********/
CFRunLoopRef CFRunLoopGetMain(void) {
CHECK_FOR_FORK();
static CFRunLoopRef __main = NULL; // no retain needed
//pthread_main_thread_np 主线程
if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np()); // no CAS needed
return __main;
}
// 保存RunLoop的全局字典,key 是 pthread_t, value 是 CFRunLoopRef
static CFMutableDictionaryRef __CFRunLoops = NULL;
/*********_CFRunLoopGet0 *******/
// should only be called by Foundation
// t==0 is a synonym for "main thread" that always works
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
//如果t不存在,则标记为主线程(即默认情况,默认是主线程)
if (pthread_equal(t, kNilPthreadT)) {
t = pthread_main_thread_np();
}
__CFSpinLock(&loopsLock);
if (!__CFRunLoops) {
__CFSpinUnlock(&loopsLock);
// 第一次进入时,初始化全局Dic,并先为主线程创建一个 RunLoop。
//创建全局字典,标记为kCFAllocatorSystemDefault
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
//通过主线程 创建主运行循环
CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
//利用dict,进行key-value绑定 *** 作,即可以说明,线程和runloop是一一对应的
// dict : key value
CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
CFRelease(dict);
}
CFRelease(mainLoop);
__CFSpinLock(&loopsLock);
}
//通过其他线程获取runloop
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
__CFSpinUnlock(&loopsLock);
if (!loop) {
//如果没有获取到,则新建一个运行循环(所以子线程不获取就不创建RunLoop)
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
__CFSpinLock(&loopsLock);
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
if (!loop) {
//将新建的runloop 与 线程进行key-value绑定
CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
loop = newLoop;
}
// don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it
__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);
}
}
return loop;
}
从上面的代码可以看出
Runloop
只有两种,一种是主线程
的, 一个是其他线程
的线程
和RunLoop
之间是一一对应
的,其关系是保存在一个全局的 Dictionary
里。主线程
的RunLoop
已经自动获取(创建)
,子线程
默认没有开启RunLoop
RunLoop的创建
根据CFRunLoopRef
的定义得知RunLoop为一个对象
,通过__CFRunLoop结构体
进行创建
typedef struct __CFRunLoop * CFRunLoopRef;
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; // runloop和线程是一一对应关系,每个runloop内部会保留一个对应的线程
uint32_t _winthread;
CFMutableSetRef _commonModes;//标记为common的mode的集合
CFMutableSetRef _commonModeItems;//commonMode的item集合
CFRunLoopModeRef _currentMode;// 当前的模式
CFMutableSetRef _modes;// CFRunLoopModeRef类型的集合,相对NSArray有序,Set为无序集合
struct _block_item *_blocks_head;
struct _block_item *_blocks_tail;
CFTypeRef _counterpart;
};
/********** __CFRunLoopMode *************/
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;
};
根据__CFRunLoop
与__CFRunLoopMode
结构体属性可以看出,每个RunLoop
中包含了多个Mode
,Mode
里面又包含了多个Source/Observer/Timer
,其中source
分为source0
和source1
, 多个mode
里,有且仅有一个
为currentMode
。如果要切换Mode
,只能退出当前 Loop
,再重新指定一个Mode
进入。这样做主要是为了分隔开不同组的 Source/Timer/Observer
,让其互不影响
。
###ModeItem
Source & Timer & Observer
统称为item
,一个item
可以被同时加入多个mode
。但一个 item
被重复加入同一个 mode
时是不会有效果的。如果一个mode
中一个item
都没有,则RunLoop
会直接退出,不进入循环
Source
表示可以唤醒RunLoop
的一些事件,基于CFRunLoopSourceRef
,是事件产生的地方
,分为source0
与source1
source0
:表示非系统事件
,即用户自定义的事件,只包含了一个回调
,它并不能主动触发
事件。使用时,你需要先调用 CFRunLoopSourceSignal(source)
,将这个Source
标记为待处理
,然后手动调用CFRunLoopWakeUp(runloop)
来唤醒RunLoop
,让其处理这个事件。source1
:表示系统事件
,主要负责底层的通讯
,具备唤醒能力
,包含了一个 mach_port
和一个回调
,被用于通过内核和其他线程
相互发送消息。这种Source
能主动唤醒RunLoop
的线程。 Timer
:就是常用NSTimer定时器
这一类,基于CFRunLoopTimerRef
,是基于时间的触发器
,它和NSTimer
是toll-free bridged
的,可以混用。其包含一个时间长度
和一个回调(函数指针)
。当其加入到 RunLoop
时,RunLoop
会注册对应的时间点
,当时间点到时,RunLoop
会被唤醒以执行那个回调。
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; /* immutable */
CFRunLoopTimerCallBack _callout; // 回调
CFRunLoopTimerContext _context; // 回调内容
};
Observer
: 主要用于监听RunLoop的状态变化
,并作出一定响应,基于CFRunLoopObserverRef
,是观察者
,结构体内部包含着一个_runloop
成员, 表明每个Observer
同时只能监听一个RunLoop
, 每个 Observer
都包含了一个回调
,当 RunLoop
的状态发生变化
时,观察者就能通过回调
接受到这个变化,主要有这几个状态:
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
//进入RunLoop
kCFRunLoopEntry = (1UL << 0),
//即将处理Timers
kCFRunLoopBeforeTimers = (1UL << 1),
//即将处理Source
kCFRunLoopBeforeSources = (1UL << 2),
//即将进入休眠
kCFRunLoopBeforeWaiting = (1UL << 5),
//被唤醒
kCFRunLoopAfterWaiting = (1UL << 6),
//退出RunLoop
kCFRunLoopExit = (1UL << 7),
kCFRunLoopAllActivities = 0x0FFFFFFFU
};
Item类型
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处理不同源的图示
CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName);
CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName);
CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode);
CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName);
CFRunLoopRemoveObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName);
CFRunLoopRemoveTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode);
以Timer
为例
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);
// 重点 : kCFRunLoopCommonModes
if (modeName == kCFRunLoopCommonModes) {
//如果是kCFRunLoopCommonModes 类型
CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
if (NULL == rl->_commonModeItems) {
rl->_commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
}
//runloop与mode 是一对多的, mode与item也是一对多的
CFSetAddValue(rl->_commonModeItems, rlt);
if (NULL != set) {
CFTypeRef context[2] = {rl, rlt};
/* add new item to all common-modes */
//执行
CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
CFRelease(set);
}
} else {
//如果是非commonMode类型
//查找runloop的模型
CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, true);
if (NULL != rlm) {
if (NULL == rlm->_timers) {
CFArrayCallBacks cb = kCFTypeArrayCallBacks;
cb.equal = NULL;
rlm->_timers = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &cb);
}
}
//判断mode是否匹配
if (NULL != rlm && !CFSetContainsValue(rlt->_rlModes, rlm->_name)) {
__CFRunLoopTimerLock(rlt);
if (NULL == rlt->_runLoop) {
rlt->_runLoop = rl;
} else if (rl != rlt->_runLoop) {
__CFRunLoopTimerUnlock(rlt);
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
return;
}
// 如果匹配,则将runloop加进去,而runloop的执行依赖于 [runloop run]
CFSetAddValue(rlt->_rlModes, rlm->_name);
__CFRunLoopTimerUnlock(rlt);
__CFRunLoopTimerFireTSRLock();
__CFRepositionTimerInMode(rlm, rlt, false);
__CFRunLoopTimerFireTSRUnlock();
if (!_CFExecutableLinkedOnOrAfter(CFSystemVersionLion)) {
// Normally we don't do this on behalf of clients, but for
// backwards compatibility due to the change in timer handling...
if (rl != CFRunLoopGetCurrent()) CFRunLoopWakeUp(rl);
}
}
if (NULL != rlm) {
__CFRunLoopModeUnlock(rlm);
}
}
__CFRunLoopUnlock(rl);
}
RunLoop 执行流程
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */
// 通知Observers, 进入RunLoop
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
// RunLoop里面具体要做的事情, 主要是一个循环
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
// 通知Observers, 退出 RunLoop
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
return result;
}
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
mach_port_name_t dispatchPort = MACH_PORT_NULL;
// 当前是否为主线程
bool cond1 = pthread_main_np();
// _CFGetTSD(__CFTSDKeyIsInGCDMainQ) 从预先分配的插槽中获取主线程的一些特定数据
// CF源码中没有搜索到_CFSetTSD(__CFTSDKeyIsInGCDMainQ)的调用,推测但不完全确定_CFGetTSD(__CFTSDKeyIsInGCDMainQ) 为空
bool cond2 = 0 == _CFGetTSD(__CFTSDKeyIsInGCDMainQ);
// 当前是否为主线程
bool cond3 = CFRunLoopGetMain() == rl;
// 当前的mode包含在commonModes里面
bool cond4 = CFSetContainsValue(rl->_commonModes, rlm->_name);
// 当前为主线程且mode被标记为common,dispatchPort才会被赋值
if (cond1 && cond2 && cond3 && cond4) {
dispatchPort = _dispatch_get_main_queue_port_4CF();
}
Boolean didDispatchPortLastTime = true;
int32_t retVal = 0;
do {
__CFRunLoopUnsetIgnoreWakeUps(rl);
// 通知observers, 即将处理Timers
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
// 通知observers, 即将处理Sources
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
// 处理Blocks
__CFRunLoopDoBlocks(rl, rlm);
// 处理source0
if (__CFRunLoopDoSources0(rl, rlm, stopAfterHandle)) {
// 处理Blocks
__CFRunLoopDoBlocks(rl, rlm);
}
// timeout 传入0 表示立即返回 传入TIMEOUT_INFINITY 表示等待到消息再返回
// 当前为主线程且mode被标记为common,且非第一次循环
if (dispatchPort && !didDispatchPortLastTime) {
// 主线程里有消息处理,直接跳转到handle_msg
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
goto handle_msg;
}
}
didDispatchPortLastTime = false;
// 通知observers, 即将进入休眠
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
__CFRunLoopSetSleeping(rl);
// 线程进入休眠,等待唤醒
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
__CFRunLoopUnsetSleeping(rl);
// 通知observers, 已经结束休眠
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
// 处理消息
handle_msg:;
if (msg_is_timer) {
// 处理timer
__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())
} else if (msg_is_dispatch) {
// 处理GCD 如果有dispatch到main_queue的block,执行block
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
} else {
// 处理Source1
__CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply)
}
// 处理Blocks
__CFRunLoopDoBlocks(rl, rlm);
// 设置返回值
if (sourceHandledThisLoop && stopAfterHandle) {
//stopAfterHandle 是传入的标记,如果处理完事件就返回
retVal = kCFRunLoopRunHandledSource;
} else if (timeout_context->termTSR < mach_absolute_time()) {
//设置了超时时间,超时返回
retVal = kCFRunLoopRunTimedOut;
} else if (__CFRunLoopIsStopped(rl)) {
// 被外部调用者强制停止了
__CFRunLoopUnsetStopped(rl);
retVal = kCFRunLoopRunStopped;
} else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
// 内部source,observers,timers 都空了,返回
retVal = kCFRunLoopRunFinished;
}
// 如果不进入上述的条件,继续循环
} while (0 == retVal);
if (timeout_timer) {
dispatch_source_cancel(timeout_timer);
dispatch_release(timeout_timer);
} else {
free(timeout_context);
}
return retVal;
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)