一、概念
?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.控制線程生命周期(AFNetWorking)
? ? 2.解決NStimer在滑動時停止工作問題
? ? 3.性能優(yōu)化,
? ? 4.監(jiān)控應用卡頓