Runloop詳解

一、概念


?runloop 程序在運行過程中循環(huán)的做一些事情;

二、 作用


處理下面的事件:

?? ? ? ? ? 定時器

?? ? ? ? ? GCD

?? ? ? ? ? 事件相應識別,頁面刷新

?? ? ? ? ? 網(wǎng)絡請求

?? ? ? ? ? atuoReleasPool

?1.程序不會馬上退出,而是保持運行狀態(tài)

?2.處理APP中的各種事件(比如觸摸事件、定時器事件等)

?3.節(jié)省cpu資源,提高程序性能:該做事做事該休息休息

三.具體操作:


UIApplicationMain? 創(chuàng)建runloop;已經(jīng)main函數(shù)的runloop

?偽代碼如下:

?? ? ? ? int retVal = 0;

?? ? ? ? do {

?? ? ? ? ? //睡眠中等待消息

?? ? ? ? ? ? int message = sleep_and_wait();

?//? ? ? ? 處理消息

?? ? ? ? ? ? retVal = perccess_message(message);


?? ? ? ? } while (retVal ==0);

?? ? ? ? return 0;

?四:獲取runloop的api


?? NSRunLoop? 是基于CFRunLoopRef的一層oc包裝

?? CFRunLoopRef 是開源的:https://opensource.apple.com/tarballs/CF/

五、總結(jié)


?1.每條線程都有唯一的一個與之對應的RunLoop對象;

?2.RunLoop保存在一個全局的dictinary里面,線程作為key,RunLoop作為value

?3.線程剛創(chuàng)建時并沒有runloop對象,runloop會在第一次獲取它時創(chuàng)建

?4.runloop會在線程結(jié)束時銷毀

?5.主線程的runloop已經(jīng)自動獲取創(chuàng)建,子線程默認沒有開啟runloop

從底層代碼分析看出來:

?if (!loop) {

? CFRunLoopRef newLoop = __CFRunLoopCreate(t);

? ? ? __CFLock(&loopsLock);

? loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));

?在獲取runloop的如果沒有runloop,會創(chuàng)建一個new的runloop對象,并且從? loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));

這句看,是把線程作為key,Runloop作為value存在字典里面去,

在子線程開啟:

? ? ? ? ? ? [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc]init] forMode:NSDefaultRunLoopMode];

? ? ? ? ? ? ? ? [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];

六 、runloop底層結(jié)構(gòu)分析

runloop對象的基本結(jié)構(gòu) 跟runloop相關的5個類:

?CFRunLoopRef

?CFRunLoopModeRef

?CFRunLoopsourceRef

?CFRunLoopTimerRef

?CFRunLoopObserverRef



?struct __CFRunLoop {

?=========關鍵的5個成員變量

? ? ? pthread_t _pthread;

? ? ? CFMutableSetRef _commonModes;

? ? ? CFMutableSetRef _commonModeItems;

? ? ? CFRunLoopModeRef _currentMode;

? ? ? CFMutableSetRef _modes;

?};


?struct __CFRunLoopMode {

?? ? CFStringRef _name;

?? ? CFMutableSetRef _sources0;

?? ? CFMutableSetRef _sources1;

?? ? CFMutableArrayRef _observers;

?? ? CFMutableArrayRef _timers;

?};

從底層結(jié)構(gòu)看出來:

1)、結(jié)構(gòu)分析:

?1.CFRunLoopModeRef代表runloop的運行模式;

?2.一個runloop包含若干個Mode,每個Mode又包含若干個soure0/soure1/timer/ObserVer;

?3.runloop啟動時只能選擇其中一個mode,作為CurrentMode

?4.如果需要切換mode,只能退出當前Loop,再重新選擇一個Mode進入;

?? ? 這樣的作用是將不同組的source0/source1/Timer/Observer能分隔開來,互不影響

?5.如果mode里面沒有任何soure0/soure1/Timer/Observer,runloop會立即退出;

? ? 注:此處的退出并不是runloop退出while循環(huán),不會導致程序退出,而是,在while循環(huán)里面進行model的切換;

2)、mode的類型有

kCFRunLoopDefaultMode;NSDefaultRunLoopMode;APP的默認mode,通常主線程是在這個mode下運行;

?UITrackingRunLoopMode;? 滾動model? 一幫常見的是這2種模式;界面跟蹤Mode,用于scrollView追蹤觸摸滑動,保證頁面滑動時不受其他Mode影響;

?kCFRunLoopCommonModes? 默認包括kCFRunLoopDefaultMode、UITrackingRunLoopMode;2種模式都兼容

//? ? UITrackingRunLoopMode\NSDefaultRunLoopMode才是真正存在的模式

//? ? NSRunLoopCommonModes并不是一個真正的模式,而是一個標記;

//? ? timer在設置了common標記的模式下都可以運行;

GSEventReceiveRunLoopMode

3)、modle內(nèi)部參數(shù)說明

source0

? ? ? ? 1.觸摸事件處理

? ? ? ? 2.performSelector:onThread:

? ? source1

?? ? ? 1.基于Port的線程間通信;

?? ? ? 2.系統(tǒng)事件捕捉,(先通過soure1處理,然后分發(fā)到soure0來處理);

? ? ? Timers

? ? ? ? [self performSelector:@selector(playInputClick) withObject:self afterDelay:2];

? ? ? Obervers

? ? ? ? 用于監(jiān)聽RunLoop的狀態(tài);(休眠或者喚醒)

? ? ? ? UI刷新(BeforeWaiting)

? ? ? ? Autorelease Pool

七:Obervers? 自己添加


?? ? ? ? typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {

?? ? ? ? ? ? kCFRunLoopEntry = (1UL << 0), //即將進入RunLoop

?? ? ? ? ? ? kCFRunLoopBeforeTimers = (1UL << 1),? // 即將處理Timer

?? ? ? ? ? ? kCFRunLoopBeforeSources = (1UL << 2), // 即將處理Source

?? ? ? ? ? ? kCFRunLoopBeforeWaiting = (1UL << 5), // 即將進入休眠

?? ? ? ? ? ? kCFRunLoopAfterWaiting = (1UL << 6), // 剛從休眠中喚醒

?? ? ? ? ? ? kCFRunLoopExit = (1UL << 7),? ? ? ? ? ? // 即將退出Loop

?? ? ? ? ? ? kCFRunLoopAllActivities = 0x0FFFFFFFU

?? ? ? ? };

方法一:

? ? CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault,kCFRunLoopAllActivities, YES, 0, observeRunLoopActicities, NULL);

? ? CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);

? ? CFRelease(observer);

方法二:

? ? CFRunLoopObserverRef observer1 = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {


? ? ? ? switch(activity) {

? ? ? ? ? ? case kCFRunLoopEntry:

? ? ? ? ? ? ? ? NSLog(@"kCFRunLoopEntry");

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? case kCFRunLoopBeforeTimers:

? ? ? ? ? ? ? ? NSLog(@"kCFRunLoopBeforeTimers");

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? case kCFRunLoopBeforeSources:

? ? ? ? ? ? ? ? NSLog(@"kCFRunLoopBeforeSources");

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? case kCFRunLoopBeforeWaiting:

? ? ? ? ? ? ? ? NSLog(@"kCFRunLoopBeforeWaiting");

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? case kCFRunLoopAfterWaiting:

? ? ? ? ? ? ? ? NSLog(@"kCFRunLoopAfterWaiting");

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? casekCFRunLoopExit:

? ? ? ? ? ? ? ? NSLog(@"kCFRunLoopExit");

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? default:

? ? ? ? ? ? ? ? break;

? ? ? ? }

? ? });

? ? CFRunLoopAddObserver(CFRunLoopGetMain(), observer1, kCFRunLoopCommonModes);

? ? CFRelease(observer1);

//? ? 處理block的邏輯

? ? CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{



? ? });

八 、runloop底層原理分析

1)、入口函數(shù)

?SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {

?//通知Oberservers進入loop

?? ? if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);

?//? 具體要做的事情

?? ? result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);


?//? ? 通知Oberserver:退出Loop

?? ? if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);

?? ? return result;

?}

2)、__CFRunLoopRun內(nèi)部函數(shù)分析

?SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {

?//通知Oberservers進入loop

?? ? if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);

?//? 具體要做的事情

?? ? result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);

?//? ? 通知Oberserver:退出Loop

?? ? if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);

?? ? return result;

?}


?__CFRunLoopRun這個函數(shù)是loop的關鍵處理的代碼邏輯


?static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {


?? ? int32_t retVal = 0;

?? ? do {

?// 通知observers:即將處理timer

?? ? ? ? __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);

?// 通知observers:即將處理Sources

? ? ? ? __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);

?//處理block;

?? ? __CFRunLoopDoBlocks(rl, rlm);

?//處理soure0

?? ? if ( __CFRunLoopDoSources0(rl, rlm, stopAfterHandle)) {

?? ? ? ? //處理block;

?? ? ? ? ? ? __CFRunLoopDoBlocks(rl, rlm);

? ? ? ? }

?//判斷有沒有soure1

?? ? if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {

?//? ? ? ? 如果有soure1,就跳轉(zhuǎn)到handle_msg

?? ? ? ? ? ? ? ? goto handle_msg;

?? ? ? ? ? ? }

?// 通知OberVers:即將休眠

? ? __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);

?? ? __CFRunLoopSetSleeping(rl);

?//? 等待別的消息來喚醒當前線程

?? ? __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);


?? ? ? ? __CFRunLoopUnsetSleeping(rl);

?//? 通知observe:休眠

? ? ? ? __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);

?handle_msg:;

? ? ? ? if (被timer喚醒) {

?//? ? ? ? ? 處理timer

? ? ? ? ? ? __CFRunLoopDoTimers(rl, rlm, mach_absolute_time())

? ? ? ? }

?? ? ? ? else if (被GCD喚醒) {

?//? ? ? ? ? 處理GCD

?? ? ? ? ? ? __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);


?? ? ? ? } else { //被soure1喚醒

?//? ? ? ? 處理soure1

? ? ? ? ? __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop;

?? ? ? ? }

?//再次處理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;

?}

3)、休眠函數(shù)分析

?__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);

休眠是線程阻塞,cup不會給它分配資源,代碼不會往下執(zhí)行;當前線程休息,CUP也休息;用戶態(tài)切換到內(nèi)核態(tài);

?具體的實現(xiàn):

?mach_msg();直接sleep;內(nèi)核層面的休息;

?用戶態(tài)? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 內(nèi)核態(tài)

? mach_msg()------->? ? mach_msg()

?等待消息

?沒有消息就讓當前線程休息

?有消息讓線程喚醒

?whiel(1);這也是一種阻塞,當前線程沒有休息還是在執(zhí)行,CUP沒有休息;

總結(jié)如圖所示:


圖1-1


九、實際應用場景

? ? 1.控制線程生命周期(AFNetWorking)

? ? 2.解決NStimer在滑動時停止工作問題

? ? 3.性能優(yōu)化,

? ? 4.監(jiān)控應用卡頓

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容