RunLoop數(shù)據(jù)結(jié)構(gòu)含懊、RunLoop的實現(xiàn)機制、RunLoop的Mode衅胀、RunLoop與NSTimer和線程

2019 iOS面試題大全---全方面剖析面試
RunLoop概念
RunLoop的數(shù)據(jù)結(jié)構(gòu)
RunLoop的Mode
RunLoop的實現(xiàn)機制
RunLoop與NSTimer
RunLoop和線程

一岔乔、RunLoop概念

RunLoop是通過內(nèi)部維護的事件循環(huán)(Event Loop)來對事件/消息進行管理的一個對象。

1滚躯、沒有消息處理時雏门,休眠已避免資源占用嘿歌,由用戶態(tài)切換到內(nèi)核態(tài)(CPU-內(nèi)核態(tài)和用戶態(tài))
2、有消息需要處理時茁影,立刻被喚醒宙帝,由內(nèi)核態(tài)切換到用戶態(tài)

為什么main函數(shù)不會退出?
int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

UIApplicationMain內(nèi)部默認開啟了主線程的RunLoop募闲,并執(zhí)行了一段無限循環(huán)的代碼(不是簡單的for循環(huán)或while循環(huán))

//無限循環(huán)代碼模式(偽代碼)
int main(int argc, char * argv[]) {        
    BOOL running = YES;
    do {
        // 執(zhí)行各種任務(wù)步脓,處理各種事件
        // ......
    } while (running);

    return 0;
}

UIApplicationMain函數(shù)一直沒有返回,而是不斷地接收處理消息以及等待休眠浩螺,所以運行程序之后會保持持續(xù)運行狀態(tài)靴患。

二、RunLoop的數(shù)據(jù)結(jié)構(gòu)

NSRunLoop(Foundation)CFRunLoop(CoreFoundation)的封裝要出,提供了面向?qū)ο蟮腁PI
RunLoop 相關(guān)的主要涉及五個類:

CFRunLoop:RunLoop對象
CFRunLoopMode:運行模式
CFRunLoopSource:輸入源/事件源
CFRunLoopTimer:定時源
CFRunLoopObserver:觀察者

1鸳君、CFRunLoop

pthread(線程對象,說明RunLoop和線程是一一對應(yīng)的)患蹂、currentMode(當前所處的運行模式)或颊、modes(多個運行模式的集合)、commonModes(模式名稱字符串集合)况脆、commonModelItems(Observer,Timer,Source集合)構(gòu)成

2饭宾、CFRunLoopMode

由name批糟、source0格了、source1、observers徽鼎、timers構(gòu)成

3盛末、CFRunLoopSource

分為source0和source1兩種

  • source0:
    即非基于port的,也就是用戶觸發(fā)的事件否淤。需要手動喚醒線程悄但,將當前線程從內(nèi)核態(tài)切換到用戶態(tài)
  • source1:
    基于port的,包含一個 mach_port 和一個回調(diào)石抡,可監(jiān)聽系統(tǒng)端口和通過內(nèi)核和其他線程發(fā)送的消息檐嚣,能主動喚醒RunLoop,接收分發(fā)系統(tǒng)事件啰扛。
    具備喚醒線程的能力
4嚎京、CFRunLoopTimer

基于時間的觸發(fā)器,基本上說的就是NSTimer隐解。在預(yù)設(shè)的時間點喚醒RunLoop執(zhí)行回調(diào)鞍帝。因為它是基于RunLoop的,因此它不是實時的(就是NSTimer 是不準確的煞茫。 因為RunLoop只負責分發(fā)源的消息帕涌。如果線程當前正在處理繁重的任務(wù)摄凡,就有可能導致Timer本次延時,或者少執(zhí)行一次)蚓曼。

5亲澡、CFRunLoopObserver

監(jiān)聽以下時間點:CFRunLoopActivity

  • kCFRunLoopEntry
    RunLoop準備啟動
  • kCFRunLoopBeforeTimers
    RunLoop將要處理一些Timer相關(guān)事件
  • kCFRunLoopBeforeSources
    RunLoop將要處理一些Source事件
  • kCFRunLoopBeforeWaiting
    RunLoop將要進行休眠狀態(tài),即將由用戶態(tài)切換到內(nèi)核態(tài)
  • kCFRunLoopAfterWaiting
    RunLoop被喚醒,即從內(nèi)核態(tài)切換到用戶態(tài)后
  • kCFRunLoopExit
    RunLoop退出
  • kCFRunLoopAllActivities
    監(jiān)聽所有狀態(tài)
6纫版、各數(shù)據(jù)結(jié)構(gòu)之間的聯(lián)系

線程和RunLoop一一對應(yīng)谷扣, RunLoop和Mode是一對多的,Mode和source捎琐、timer会涎、observer也是一對多的


image.png

三、RunLoop的Mode

關(guān)于Mode首先要知道一個RunLoop 對象中可能包含多個Mode瑞凑,且每次調(diào)用 RunLoop 的主函數(shù)時末秃,只能指定其中一個 Mode(CurrentMode)。切換 Mode籽御,需要重新指定一個 Mode 练慕。主要是為了分隔開不同的 Source、Timer技掏、Observer铃将,讓它們之間互不影響。

image.png

當RunLoop運行在Mode1上時哑梳,是無法接受處理Mode2或Mode3上的Source劲阎、Timer、Observer事件的

總共是有五種CFRunLoopMode:

  • kCFRunLoopDefaultMode:默認模式鸠真,主線程是在這個運行模式下運行

  • UITrackingRunLoopMode:跟蹤用戶交互事件(用于 ScrollView 追蹤觸摸滑動悯仙,保證界面滑動時不受其他Mode影響)

  • UIInitializationRunLoopMode:在剛啟動App時第進入的第一個 Mode,啟動完成后就不再使用

  • GSEventReceiveRunLoopMode:接受系統(tǒng)內(nèi)部事件吠卷,通常用不到

  • kCFRunLoopCommonModes:偽模式锡垄,不是一種真正的運行模式,是同步Source/Timer/Observer到多個Mode中的一種解決方案

四祭隔、RunLoop的實現(xiàn)機制

image.png

這張圖在網(wǎng)上流傳比較廣货岭。
對于RunLoop而言最核心的事情就是保證線程在沒有消息的時候休眠,在有消息時喚醒疾渴,以提高程序性能千贯。RunLoop這個機制是依靠系統(tǒng)內(nèi)核來完成的(蘋果操作系統(tǒng)核心組件Darwin中的Mach)。

image.png

RunLoop通過mach_msg()函數(shù)接收程奠、發(fā)送消息丈牢。它的本質(zhì)是調(diào)用函數(shù)mach_msg_trap(),相當于是一個系統(tǒng)調(diào)用瞄沙,會觸發(fā)內(nèi)核狀態(tài)切換己沛。在用戶態(tài)調(diào)用 mach_msg_trap()時會切換到內(nèi)核態(tài)慌核;內(nèi)核態(tài)中內(nèi)核實現(xiàn)的mach_msg()函數(shù)會完成實際的工作。
即基于port的source1申尼,監(jiān)聽端口垮卓,端口有消息就會觸發(fā)回調(diào);而source0师幕,要手動標記為待處理和手動喚醒RunLoop

Mach消息發(fā)送機制
大致邏輯為:
1粟按、通知觀察者 RunLoop 即將啟動。
2霹粥、通知觀察者即將要處理Timer事件灭将。
3、通知觀察者即將要處理source0事件后控。
4庙曙、處理source0事件。
5浩淘、如果基于端口的源(Source1)準備好并處于等待狀態(tài)捌朴,進入步驟9。
6张抄、通知觀察者線程即將進入休眠狀態(tài)砂蔽。
7、將線程置于休眠狀態(tài)署惯,由用戶態(tài)切換到內(nèi)核態(tài)左驾,直到下面的任一事件發(fā)生才喚醒線程。

  • 一個基于 port 的Source1 的事件(圖里應(yīng)該是source0)泽台。
  • 一個 Timer 到時間了什荣。
  • RunLoop 自身的超時時間到了。
  • 被其他調(diào)用者手動喚醒怀酷。

8、通知觀察者線程將被喚醒嗜闻。
9蜕依、處理喚醒時收到的事件。

  • 如果用戶定義的定時器啟動琉雳,處理定時器事件并重啟RunLoop样眠。進入步驟2。
  • 如果輸入源啟動翠肘,傳遞相應(yīng)的消息檐束。
  • 如果RunLoop被顯示喚醒而且時間還沒超時,重啟RunLoop束倍。進入步驟2

10被丧、通知觀察者RunLoop結(jié)束盟戏。

五、RunLoop與NSTimer

一個比較常見的問題:滑動tableView時甥桂,定時器還會生效嗎柿究?
默認情況下RunLoop運行在kCFRunLoopDefaultMode下,而當滑動tableView時黄选,RunLoop切換到UITrackingRunLoopMode蝇摸,而Timer是在kCFRunLoopDefaultMode下的,就無法接受處理Timer的事件办陷。
怎么去解決這個問題呢貌夕?把Timer添加到UITrackingRunLoopMode上并不能解決問題,因為這樣在默認情況下就無法接受定時器事件了民镜。
所以我們需要把Timer同時添加到UITrackingRunLoopModekCFRunLoopDefaultMode上蜂嗽。
那么如何把timer同時添加到多個mode上呢?就要用到NSRunLoopCommonModes

[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

Timer就被添加到多個mode上殃恒,這樣即使RunLoop由kCFRunLoopDefaultMode切換到UITrackingRunLoopMode下植旧,也不會影響接收Timer事件

六、RunLoop和線程

  • 線程和RunLoop是一一對應(yīng)的,其映射關(guān)系是保存在一個全局的 Dictionary 里
  • 自己創(chuàng)建的線程默認是沒有開啟RunLoop的
1离唐、怎么創(chuàng)建一個常駐線程病附?

1、為當前線程開啟一個RunLoop(第一次調(diào)用 [NSRunLoop currentRunLoop]方法時實際是會先去創(chuàng)建一個RunLoop)
1亥鬓、向當前RunLoop中添加一個Port/Source等維持RunLoop的事件循環(huán)(如果RunLoop的mode中一個item都沒有完沪,RunLoop會退出)
2、啟動該RunLoop

   @autoreleasepool {
        
        NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
        
        [[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
        
        [runLoop run];
        
    }
2嵌戈、輸出下邊代碼的執(zhí)行順序
 NSLog(@"1");

dispatch_async(dispatch_get_global_queue(0, 0), ^{
    
    NSLog(@"2");

    [self performSelector:@selector(test) withObject:nil afterDelay:10];
    
    NSLog(@"3");
});

NSLog(@"4");

- (void)test
{
    
    NSLog(@"5");
}

答案是1423覆积,test方法并不會執(zhí)行。
原因是如果是帶afterDelay的延時函數(shù)熟呛,會在內(nèi)部創(chuàng)建一個 NSTimer宽档,然后添加到當前線程的RunLoop中。也就是如果當前線程沒有開啟RunLoop庵朝,該方法會失效吗冤。
那么我們改成:

dispatch_async(dispatch_get_global_queue(0, 0), ^{
        
        NSLog(@"2");
        
        [[NSRunLoop currentRunLoop] run];
        
        [self performSelector:@selector(test) withObject:nil afterDelay:10];
  
        NSLog(@"3");
    });

然而test方法依然不執(zhí)行。
原因是如果RunLoop的mode中一個item都沒有九府,RunLoop會退出椎瘟。即在調(diào)用RunLoop的run方法后,由于其mode中沒有添加任何item去維持RunLoop的時間循環(huán)侄旬,RunLoop隨即還是會退出肺蔚。
所以我們自己啟動RunLoop,一定要在添加item后

dispatch_async(dispatch_get_global_queue(0, 0), ^{
        
        NSLog(@"2");
        
        [self performSelector:@selector(test) withObject:nil afterDelay:10];
        
        [[NSRunLoop currentRunLoop] run];
  
        NSLog(@"3");
    });
3儡羔、怎樣保證子線程數(shù)據(jù)回來更新UI的時候不打斷用戶的滑動操作宣羊?

當我們在子請求數(shù)據(jù)的同時滑動瀏覽當前頁面璧诵,如果數(shù)據(jù)請求成功要切回主線程更新UI,那么就會影響當前正在滑動的體驗段只。
我們就可以將更新UI事件放在主線程的NSDefaultRunLoopMode上執(zhí)行即可腮猖,這樣就會等用戶不再滑動頁面,主線程RunLoop由UITrackingRunLoopMode切換到NSDefaultRunLoopMode時再去更新UI

[self performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO modes:@[NSDefaultRunLoopMode]];
    
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末赞枕,一起剝皮案震驚了整個濱河市澈缺,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌炕婶,老刑警劉巖姐赡,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異柠掂,居然都是意外死亡项滑,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門涯贞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來枪狂,“玉大人,你說我怎么就攤上這事宋渔≈菁玻” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵皇拣,是天一觀的道長严蓖。 經(jīng)常有香客問我,道長氧急,這世上最難降的妖魔是什么颗胡? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮吩坝,結(jié)果婚禮上毒姨,老公的妹妹穿的比我還像新娘。我一直安慰自己钾恢,他們只是感情好手素,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著瘩蚪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪稿黍。 梳的紋絲不亂的頭發(fā)上疹瘦,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機與錄音巡球,去河邊找鬼言沐。 笑死邓嘹,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的险胰。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼希俩!你這毒婦竟也來了狞洋?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤榆综,失蹤者是張志新(化名)和其女友劉穎妙痹,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鼻疮,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡怯伊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了判沟。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片耿芹。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖挪哄,靈堂內(nèi)的尸體忽然破棺而出吧秕,到底是詐尸還是另有隱情,我是刑警寧澤中燥,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布寇甸,位于F島的核電站,受9級特大地震影響疗涉,放射性物質(zhì)發(fā)生泄漏拿霉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一咱扣、第九天 我趴在偏房一處隱蔽的房頂上張望绽淘。 院中可真熱鬧,春花似錦闹伪、人聲如沸沪铭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽杀怠。三九已至,卻和暖如春厅克,著一層夾襖步出監(jiān)牢的瞬間赔退,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留硕旗,地道東北人窗骑。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像漆枚,于是被迫代替她去往敵國和親创译。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)自http://blog.ibireme.com/2015/05/18/runloop 深入理解RunLoop ...
    飄金閱讀 975評論 0 4
  • Runloop是iOS和OSX開發(fā)中非城交基礎(chǔ)的一個概念软族,從概念開始學習。 RunLoop的概念 -般說碘橘,一個線程一...
    小貓仔閱讀 979評論 0 1
  • 轉(zhuǎn)載:http://www.cocoachina.com/ios/20150601/11970.html RunL...
    Gatling閱讀 1,435評論 0 13
  • 轉(zhuǎn)自bireme互订,原地址:https://blog.ibireme.com/2015/05/18/runloop/...
    乜_啊_閱讀 1,330評論 0 5
  • 不知何時,我發(fā)現(xiàn)
    諾何閱讀 122評論 0 0