RunLoop 原理和核心機制

RunLoop的定義

當(dāng)有持續(xù)的異步任務(wù)需求時,我們會創(chuàng)建一個獨立的生命周期可控的線程。RunLoop就是控制線程生命周期并接收事件進(jìn)行處理的機制。

RunLoop是iOS事件響應(yīng)與任務(wù)處理最核心的機制,它貫穿iOS整個系統(tǒng)。

Foundation: NSRunLoop
Core Foundation: CFRunLoop 核心部分,代碼開源,C 語言編寫,跨平臺

理解

進(jìn)程是一家工廠,線程是一個流水線,Run Loop就是流水線上的主管;當(dāng)工廠接到商家的訂單分配給這個流水線時,Run Loop就啟動這個流水線,讓流水線動起來,生產(chǎn)產(chǎn)品;當(dāng)產(chǎn)品生產(chǎn)完畢時,Run Loop就會暫時停下流水線,節(jié)約資源。
RunLoop管理流水線,流水線才不會因為無所事事被工廠銷毀;而不需要流水線時,就會辭退RunLoop這個主管,即退出線程,把所有資源釋放。

RunLoop并不是iOS平臺的專屬概念,在任何平臺的多線程編程中,為控制線程的生命周期,接收處理異步消息都需要類似RunLoop的循環(huán)機制實現(xiàn),Android的Looper就是類似的機制。

特性

  • 主線程的RunLoop在應(yīng)用啟動的時候就會自動創(chuàng)建
  • 其他線程則需要在該線程下自己啟動
  • 不能自己創(chuàng)建RunLoop
  • RunLoop并不是線程安全的,所以需要避免在其他線程上調(diào)用當(dāng)前線程的RunLoop
  • RunLoop負(fù)責(zé)管理autorelease pools
  • RunLoop負(fù)責(zé)處理消息事件,即輸入源事件和計時器事件

RunLoop機制

  • 支持接收處理輸入源(Input Source)事件,包括:
系統(tǒng)的Mach Port事件,是一種通訊事件
自定義輸入事件
  • 支持接受處理定時源(Timer)事件

在啟動RunLoop之前,必須添加監(jiān)聽的輸入源事件或者定時源事件,否則調(diào)用[runloop run]會直接返回,而不會進(jìn)入循環(huán)讓線程長駐。

如果沒有添加任何輸入源事件或Timer事件,線程會一直在無限循環(huán)空轉(zhuǎn)中,會一直占用CPU時間片,沒有實現(xiàn)資源的合理分配。

沒有while循環(huán)且沒有添加任何輸入源或Timer的線程,線程會直接完成,被系統(tǒng)回收。
//錯誤做法 
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
while (!self.isCancelled && !self.isFinished) 
{ 
[runLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]];
};
//正確做法
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
while (!self.isCancelled && !self.isFinished) 
{ 
@autoreleasepool { [runLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]]; }
}

Run Loop Modes

  • 理解
    Run Loop Mode就是流水線上支持生產(chǎn)的產(chǎn)品類型,流水線在一個時刻只能在一種模式下運行,生產(chǎn)某一類型的產(chǎn)品。消息事件就是訂單。

Cocoa定義了四中Mode

Default:NSDefaultRunLoopMode,
    默認(rèn)模式,在Run Loop沒有指定Mode的時候,默認(rèn)就跑在Default Mode下
Connection:NSConnectionReplyMode,
用來監(jiān)聽處理網(wǎng)絡(luò)請求NSConnection的事件
Modal:NSModalPanelRunLoopMode,
OS X的Modal面板事件
Event tracking:UITrackingRunLoopMode,拖動事件
Common mode:NSRunLoopCommonModes,是一個模式集合,當(dāng)綁定一個事件源到這個模式集合的時候就相當(dāng)于綁定到了集合內(nèi)的每一個模式
  • RunLoop可以通過[acceptInputForMode:beforeDate:]和[runMode:beforeDate:]來指定在一段時間內(nèi)的運行模式。如果不指定的話,RunLoop默認(rèn)會運行在Default下(不斷重復(fù)調(diào)用runMode:NSDefaultRunLoopMode beforDate:)

  • 在主線程啟動一個計時器Timer,然后拖動UITableView或者UIScrollView,計時器不執(zhí)行。這是因為,為了更好的用戶體驗,在主線程中Event tracking模式的優(yōu)先級最高。在用戶拖動控件時,主線程的Run Loop是運行在Event tracking Mode下,而創(chuàng)建的Timer是默認(rèn)關(guān)聯(lián)為Default Mode,因此系統(tǒng)不會立即執(zhí)行Default Mode下接收的事件。解決方法:

NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerFireMethod:) userInfo:nil repeats:YES]; 
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; 
//或
 [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];[timer fire];

Run Loop應(yīng)用實踐

Run Loop主要有以下三個應(yīng)用場景:
  • 維護(hù)線程的生命周期,讓線程不自動退出,isFinished為Yes時退出。
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
while (!self.isCancelled && !self.isFinished) 
{ @autoreleasepool { [runLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]]; }
}
  • 創(chuàng)建常駐線程,執(zhí)行一些會一直存在的任務(wù)。該線程的生命周期跟App相同
@autoreleasepool { 
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
 [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
 [runLoop run];
}

  • 在一定時間內(nèi)監(jiān)聽某種事件,或執(zhí)行某種任務(wù)的線程
    如下代碼,在30分鐘內(nèi),每隔30s執(zhí)行onTimerFired:。這種場景一般會出現(xiàn)在,如我需要在應(yīng)用啟動之后,在一定時間內(nèi)持續(xù)更新某項數(shù)據(jù)。
@autoreleasepool { 
NSRunLoop * runLoop = [NSRunLoop currentRunLoop]; 
NSTimer * udpateTimer = [NSTimer timerWithTimeInterval:30 target:self selector:@selector(onTimerFired:) userInfo:nil repeats:YES]; 
[runLoop addTimer:udpateTimer forMode:NSRunLoopCommonModes]; 
[runLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:60*30]];}

  • AFNetworking中RunLoop的創(chuàng)建
+ (void)networkRequestThreadEntryPoint:(id)__unused object
 { 
        @autoreleasepool { 
        [[NSThread currentThread] setName:@"AFNetworking"]; 
        NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; 
        // 這里主要是監(jiān)聽某個 port,目的是讓這個 Thread 不會回收 
        [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode]; 
        [runLoop run];
   }
}

+ (NSThread *)networkRequestThread 
{ 
        static NSThread *_networkRequestThread = nil; 
        static dispatch_once_t oncePredicate;                          dispatch_once(&oncePredicate, ^{ 
_networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil]; [_networkRequestThread start]; }); 
return _networkRequestThread;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • 原文地址 Runloop核心機制和原理 搞iOS之后一直沒有深入研究過RunLoop,非常的慚愧。剛好前一陣子負(fù)責(zé)...
    lgh_ios閱讀 454評論 0 3
  • 什么是Run Loops RunLoops是與線程相關(guān)聯(lián)的基礎(chǔ)部分,一個Run Loop就是事件處理循環(huán),他是用來...
    傻傻小蘿卜閱讀 1,103評論 0 5
  • 這是AF2.x經(jīng)典的代碼: 首先我們要明確一個概念,線程一般都是一次執(zhí)行完任務(wù),就銷毀了。 而添加了runloop...
    有夢想的老伯伯閱讀 2,085評論 5 13
  • 一、什么是runloop 字面意思是“消息循環(huán)、運行循環(huán)”。它不是線程,但它和線程息息相關(guān)。一般來講,一個線程一次...
    WeiHing閱讀 8,311評論 11 111
  • 父親節(jié)將至,不妨與他重溫年輕美好點滴,這位世上最愛的男人,潤澤無聲的關(guān)懷恰似父親的一腔柔情,低調(diào)內(nèi)斂,卻總暖至心扉!
    會呼吸的石頭閱讀 226評論 0 0

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