1.前沿
1.1概念
Runloop不僅僅是一個運(yùn)行循環(huán)(do-while循環(huán))您单,也是提供了一個入口函數(shù)的對象,消息機(jī)制處理模式砾脑。運(yùn)行循環(huán)從兩種不同類型的源接收事件兔乞。
輸入源提供異步事件,通常是來自另一個線程或來自不同應(yīng)用程序的消息凄敢。定時器源提供同步事件碌冶,發(fā)生在預(yù)定時間或重復(fù)間隔。
兩種類型的源都使用特定于應(yīng)用程序的處理程序例程來處理事件涝缝。除了處理輸入源之外扑庞,Runloop還會生成有關(guān)Runloop行為的通知。
已注冊的運(yùn)行循環(huán)觀察器可以接收這些通知并使用它們在線程上執(zhí)行其他處理拒逮。
runloop官方文檔
1.2runloop實(shí)質(zhì)做了什么事
runloop實(shí)質(zhì)是一個dowhile循環(huán)
void CFRunLoopRun(void) { /* DOES CALLOUT */
int32_t result;
do {
// 1.0e10 : 科學(xué)技術(shù) 1*10^10
result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
CHECK_FOR_FORK();
} while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}
runloop中的do while循環(huán)和我們自己寫的有什么區(qū)別呢罐氨?
while (1) {
NSLog(@"hello");
}
我們自己寫的dowhile循環(huán),cpu被大量占用滩援,但是系統(tǒng)中runloop的dowhile并沒有大量占有cpu栅隐,有此可以推斷系統(tǒng)的runloop做了優(yōu)化。
1.3.RunLoop的作用
1.保持程序的持續(xù)運(yùn)行
dowhile讓程序蓖婊玻活租悄。
2.處理APP中的各種事件(觸摸、定時器恩袱、performSelector)
當(dāng)我們點(diǎn)擊屏幕時泣棋,GraphicsServices中GSEventRunModal會想runloop發(fā)送消息,然后執(zhí)行CFRunLoopRunSpecific畔塔,然后讓UIKit處理事件
3.節(jié)省cpu資源潭辈、提供程序的性能:該做事就做事,該休息就休息
當(dāng)我們的應(yīng)用沒有事情處理時澈吨,占有的cpu幾乎為0把敢,當(dāng)我們外部或者系統(tǒng)有事情處理時被喚醒。
1.4.runloop 的item
1.block應(yīng)用:
CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK
這個item就是讓系統(tǒng)調(diào)用blcok
2.調(diào)timer:
CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION
當(dāng)runloop被這個item喚醒時棚辽,調(diào)用timer回調(diào)技竟。
3.響應(yīng)source0:
__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION
當(dāng)我們UI有事件需要處理時,比如我們實(shí)現(xiàn)touchesBegan方法屈藐,看堆棧信息
- 響應(yīng)source1: CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION
這個系統(tǒng)的榔组,筆者因?yàn)槟芰τ邢蓿詻]有找到出發(fā)的方法 - GCD主隊列:
CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE
系統(tǒng)啟動這個item联逻,然后調(diào)用GCD主隊列
image.png - observer源: CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION
當(dāng)runloop被這個item喚醒時搓扯,就是該調(diào)用observer了
image.png
2.runloop底層實(shí)現(xiàn)
2.1. runloop的結(jié)構(gòu)
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;
uint32_t _winthread;
CFMutableSetRef _commonModes;
CFMutableSetRef _commonModeItems;
CFRunLoopModeRef _currentMode;
CFMutableSetRef _modes;
struct _block_item *_blocks_head;
struct _block_item *_blocks_tail;
CFTypeRef _counterpart;
};
runloop結(jié)構(gòu)體中保存了線程,_commonModes _currentMode包归,_commonModeItems, _block_item等等信息锨推。
2.2. runloop和線程關(guān)系
當(dāng)我們調(diào)用CFRunLoopGetCurrent和CFRunLoopGetMain時到底發(fā)生了什么?我們從源碼分析CFRunLoopGetCurrent和CFRunLoopGetMain最終掉的都是_CFRunLoopGet0
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
//如果線程是空的,默認(rèn)是豬線程
if (pthread_equal(t, kNilPthreadT)) {
t = pthread_main_thread_np();
}
__CFSpinLock(&loopsLock);
//判斷靜態(tài)字典是否為空(為空說明時第一次使用)
if (!__CFRunLoops) {
//是空的創(chuàng)建换可,并創(chuàng)建主線程的runloop并保存到__CFRunLoops
__CFSpinUnlock(&loopsLock);
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
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);
__CFSpinLock(&loopsLock);
}
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
__CFSpinUnlock(&loopsLock);
if (!loop) {
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
__CFSpinLock(&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
__CFSpinUnlock(&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;
}
1.首先判斷傳入的線程是否為空椎椰,如果是空,設(shè)置成主線程
2.判斷__CFRunLoops是否為空沾鳄,__CFRunLoops是一個Dictionary 以線程為key慨飘,runloop為value進(jìn)行保存,如果為空進(jìn)行創(chuàng)建译荞,并保存main線程和主runloop瓤的,3
3.試著從__CFRunLoops以線程為key進(jìn)行取值,如果取到就在下面返回吞歼,如果取不到則進(jìn)行創(chuàng)建圈膏。并在下面返回。
2.3. runloop機(jī)制
CFRunLoopRun----》CFRunLoopRunSpecific
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */
CHECK_FOR_FORK();
if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished;
__CFRunLoopLock(rl);
/// 首先根據(jù)modeName找到對應(yīng)mode
CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
/// 通知 Observers: RunLoop 即將進(jìn)入 loop。
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
/// 內(nèi)部函數(shù),進(jìn)入loop
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
/// 通知 Observers: RunLoop 即將退出潮太。
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
return result;
}
/// 核心函數(shù)
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
int32_t retVal = 0;
do { // itmes do
/// 通知 Observers: 即將處理timer事件
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
/// 通知 Observers: 即將處理Source事件
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources)
/// 處理Blocks
__CFRunLoopDoBlocks(rl, rlm);
/// 處理sources0
Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
/// 處理sources0返回為YES
if (sourceHandledThisLoop) {
/// 處理Blocks
__CFRunLoopDoBlocks(rl, rlm);
}
/// 判斷有無端口消息(Source1)
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
/// 處理消息
goto handle_msg;
}
/// 通知 Observers: 即將進(jìn)入休眠
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
__CFRunLoopSetSleeping(rl);
/// 等待被喚醒
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
// user callouts now OK again
__CFRunLoopUnsetSleeping(rl);
/// 通知 Observers: 被喚醒,結(jié)束休眠
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
handle_msg:
if (被Timer喚醒) {
/// 處理Timers
__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())慎皱;
} else if (被GCD喚醒) {
/// 處理gcd
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
} else if (被Source1喚醒) {
/// 被Source1喚醒,處理Source1
__CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply)
}
/// 處理block
__CFRunLoopDoBlocks(rl, rlm);
if (sourceHandledThisLoop && stopAfterHandle) {
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)) {
retVal = kCFRunLoopRunFinished;
}
} while (0 == retVal);
return retVal;
}