可能碰到的iOS筆試面試題(15)--Runloop

Runloop

什么是 Runloop脚粟?

  • 從字面上講就是運(yùn)行循環(huán)覆旱。

  • 它內(nèi)部就是do-while循環(huán),在這個(gè)循環(huán)內(nèi)部不斷地處理各種任務(wù)核无。

  • 一個(gè)線程對(duì)應(yīng)一個(gè)RunLoop扣唱,主線程的RunLoop默認(rèn)已經(jīng)啟動(dòng),子線程的RunLoop得手動(dòng)啟動(dòng)(調(diào)用run方法)

  • RunLoop只能選擇一個(gè)Mode啟動(dòng),如果當(dāng)前Mode中沒有任何Source(Sources0噪沙、Sources1)炼彪、Timer,那么就直接退出RunLoop

  • 基本的作用就是保持程序的持續(xù)運(yùn)行正歼,處理app中的各種事件辐马。通過runloop,有事運(yùn)行局义,沒事就休息喜爷,可以節(jié)省cpu資源,提高程序性能萄唇。

Runloop對(duì)象

iOS中有2套API來訪問和使用RunLoop

  • Foundation:NSRunLoop

  • Core Foundation:CFRunLoopRef

  • NSRunLoop和CFRunLoopRef都代表著RunLoop對(duì)象

  • NSRunLoop是基于CFRunLoopRef的一層OC包裝檩帐,所以要了解RunLoop內(nèi)部結(jié)構(gòu),需要多研究CFRunLoopRef層面的API另萤。

Runloop與線程

  • 每條線程都有唯一的一個(gè)與之對(duì)應(yīng)的RunLoop對(duì)象

  • 主線程的RunLoop已經(jīng)自動(dòng)創(chuàng)建好了轿塔,子線程的RunLoop需要主動(dòng)創(chuàng)建

  • RunLoop在第一次獲取時(shí)創(chuàng)建,在線程結(jié)束時(shí)銷毀

獲得RunLoop對(duì)象

  • Foundation

[NSRunLoop currentRunLoop]; // 獲得當(dāng)前線程的RunLoop對(duì)象

[NSRunLoop mainRunLoop]; // 獲得主線程的RunLoop對(duì)象

  • Core Foundation

CFRunLoopGetCurrent(); // 獲得當(dāng)前線程的RunLoop對(duì)象

CFRunLoopGetMain(); // 獲得主線程的RunLoop對(duì)象

RunLoop相關(guān)類

Core Foundation中關(guān)于RunLoop的5個(gè)類
CFRunLoopRef
CFRunLoopModeRef
CFRunLoopSourceRef
CFRunLoopTimerRef
CFRunLoopObserverRef

CFRunLoopModeRef

CFRunLoopModeRef代表RunLoop的運(yùn)行模式仲墨。
一個(gè)RunLoop包含若干個(gè)Mode勾缭,每個(gè)Mode又包含若干個(gè)(set)Source/(array)Timer/(array)Observer

每次RunLoop啟動(dòng)時(shí),只能指定其中一個(gè) Mode目养,這個(gè)Mode被稱作CurrentMode

如果需要切換Mode俩由,只能退出Loop,再重新指定一個(gè)Mode進(jìn)入

這樣做主要是為了分隔開不同組的Source/Timer/Observer癌蚁,讓其互不影響

mode主要是用來指定事件在運(yùn)行循環(huán)中的優(yōu)先級(jí)的幻梯,分為:
?   NSDefaultRunLoopMode(kCFRunLoopDefaultMode):默認(rèn),空閑狀態(tài)
?   UITrackingRunLoopMode:ScrollView滑動(dòng)時(shí)會(huì)切換到該Mode
?   UIInitializationRunLoopMode:run loop啟動(dòng)時(shí)努释,會(huì)切換到該mode
?   NSRunLoopCommonModes(kCFRunLoopCommonModes):Mode集合
蘋果公開提供的Mode有兩個(gè):
?   NSDefaultRunLoopMode(kCFRunLoopDefaultMode)
?   NSRunLoopCommonModes(kCFRunLoopCommonModes)

CFRunLoopTimerRef

  • CFRunLoopTimerRef是基于時(shí)間的觸發(fā)器

  • CFRunLoopTimerRef基本上說的就是NSTimer碘梢,它受RunLoop的Mode影響

  • GCD的定時(shí)器不受RunLoop的Mode影響

CFRunLoopSourceRef

CFRunLoopSourceRef是事件源(輸入源)
按照官方文檔,Source的分類
Port-Based Sources
Custom Input Sources
Cocoa Perform Selector Sources
按照函數(shù)調(diào)用棧伐蒂,Source的分類
Source0:非基于Port的
Source1:基于Port的煞躬,通過內(nèi)核和其他線程通信,接收逸邦、分發(fā)系統(tǒng)事件

CFRunLoopObserverRef

  • CFRunLoopObserverRef是觀察者恩沛,能夠監(jiān)聽RunLoop的狀態(tài)改變

  • 可以監(jiān)聽的時(shí)間點(diǎn)有以下幾個(gè)

    • kcfRunLoopEntry(即將進(jìn)入loop)//1

    • kcfRunLoopBeforeTimers(即將處理timer)//2

    • kcfRunLoopBeforeSources(即將處理source)//4

    • kcfRunLoopBeforeWaiting(即將進(jìn)入休眠)//32

    • kcfRunLoopAfterWaiting(剛從休眠中喚醒)//64

    • kcfRunLoopExit(即將退出loop)//128

  • 添加Observer

    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
    NSLog(@"----監(jiān)聽到RunLoop狀態(tài)發(fā)生改變---%zd", activity);
});

// 添加觀察者:監(jiān)聽RunLoop的狀態(tài)
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);

// 釋放Observer
CFRelease(observer);

RunLoop處理邏輯

  • 通知Observer:即將進(jìn)入Loop(1)

  • 通知Observer:將要處理Timer(2)

  • 通知Observer:將要處理Source0(3)

  • 處理Source0(4)

  • 如果有Source0,跳到第9步(5)

  • 通知Observer:線程即將休眠(6)

  • 休眠缕减,等待喚醒:(7)

    • Source0(port)雷客。

    • timer啟動(dòng)

    • RunLoop設(shè)置的timer已經(jīng)超時(shí)

    • Runloop被外部手動(dòng)喚醒

  • 通知Observer:線程將被喚醒(8)

  • 處理未處理的時(shí)間(9)

    • 如果用戶定義的定時(shí)器啟動(dòng),處理定時(shí)器事件并重啟Runloop桥狡。進(jìn)入步驟2.

    • 如果輸入源啟動(dòng)搅裙,傳遞相應(yīng)的消息皱卓。

    • 如果RunLopp被顯式喚醒而且時(shí)間還沒超時(shí),重啟RunLoop部逮,進(jìn)入步驟2.

  • 通知Observer:即將退出Loop

Runloop的應(yīng)用

  • NSTimer
  • ImageView顯示
  • PerformSelector
  • 常駐線程
  • 自動(dòng)釋放池

runloop定時(shí)源和輸入源
image
image

  • Runloop處理的輸入事件有兩種不同的來源:輸入源(input source)和定時(shí)源(timer source)

  • 輸入源傳遞異步消息娜汁,通常來自于其他線程或者程序。

  • 定時(shí)源則傳遞同步消息甥啄,在特定時(shí)間或者一定的時(shí)間間隔發(fā)生

NSRunLoop的實(shí)現(xiàn)機(jī)制,及在多線程中如何使用

- 實(shí)現(xiàn)機(jī)制:回答runloop的基本作用,處理邏輯炬搭,前面都有蜈漓。

- 程序創(chuàng)建子線程的時(shí)候,才需要手動(dòng)啟動(dòng)runloop宫盔。主線程的runloop已經(jīng)默認(rèn)啟動(dòng)融虽。

- 在多線程中,你需要判斷是否需要runloop灼芭。如果需要runloop有额,那么你要負(fù)責(zé)配置runloop并啟動(dòng)。你不需要在任何情況下都去啟動(dòng)runloop彼绷。比如巍佑,你使用線程去處理一個(gè)預(yù)先定義好的耗時(shí)極長的任務(wù)時(shí),你就可以無需啟動(dòng)runloop寄悯。Runloop只在你要和線程有交互時(shí)才需要

runloop和線程有什么關(guān)系萤衰?

- 主線程的run loop默認(rèn)是啟動(dòng)的。
iOS的應(yīng)用程序里面猜旬,程序啟動(dòng)后會(huì)有一個(gè)如下的main()函數(shù)
( argc,  * argv[]) {
@autoreleasepool {
    return UIApplicationMain(argc, argv, , NSStringFromClass([AppDelegate class]));
}
}
重點(diǎn)是UIApplicationMain()函數(shù)脆栋,這個(gè)方法會(huì)為main thread設(shè)置一個(gè)NSRunLoop對(duì)象,這就解釋了:為什么我們的應(yīng)用可以在無人操作的時(shí)候休息洒擦,需要讓它干活的時(shí)候又能立馬響應(yīng)椿争。

- 對(duì)其它線程來說,runloop默認(rèn)是沒有啟動(dòng)的熟嫩,runloop只在你要和線程有交互時(shí)才需要秦踪。

- 在任何一個(gè) Cocoa 程序的線程中,都可以通過以下代碼來獲取到當(dāng)前線程的 run loop 掸茅。
NSRunLoop *runloop = [NSRunLoop currentRunLoop];

autorelease 對(duì)象在什么情況下會(huì)被釋放洋侨?

  • 分兩種情況:手動(dòng)干預(yù)釋放和系統(tǒng)自動(dòng)釋放
  • 手動(dòng)干預(yù)釋放就是指定autoreleasepool,當(dāng)前作用域大括號(hào)結(jié)束就立即釋放
  • 系統(tǒng)自動(dòng)去釋放:不手動(dòng)指定autoreleasepool,Autorelease對(duì)象會(huì)在當(dāng)前的 runloop 迭代結(jié)束時(shí)釋放
  • kCFRunLoopEntry(1):第一次進(jìn)入會(huì)自動(dòng)創(chuàng)建一個(gè)autorelease
  • kCFRunLoopBeforeWaiting(32):進(jìn)入休眠狀態(tài)前會(huì)自動(dòng)銷毀一個(gè)autorelease,然后重新創(chuàng)建一個(gè)新的autorelease
  • kCFRunLoopExit(128):退出runloop時(shí)會(huì)自動(dòng)銷毀最后一個(gè)創(chuàng)建的autorelease

對(duì)于runloop的理解不正確的是

A 每一個(gè)線程都有其對(duì)應(yīng)的RunLoop
B 默認(rèn)非主線程的RunLoop是沒有運(yùn)行的
C 在一個(gè)單獨(dú)的線程中沒有必要去啟用RunLoop
D 可以將NSTimer添加到runloop中
  • 參考答案:C
  • 理由:說到RunLoop,它可是多線程的法定倦蚪。通常來說希坚,一個(gè)線程一次只能執(zhí)行一個(gè)任務(wù),執(zhí)行完任務(wù)后就會(huì)退出線程陵且。但是裁僧,對(duì)于主線程是不能退出的个束,因此我們需要讓主線程即時(shí)任務(wù)執(zhí)行完畢,也可以繼續(xù)等待是接收事件而不退出聊疲,那么RunLoop就是關(guān)鍵法寶了茬底。但是非主線程通常來說就是為了執(zhí)行某一任務(wù)的,執(zhí)行完畢就需要?dú)w還資源获洲,因此默認(rèn)是不運(yùn)行RunLoop的阱表。NSRunLoop提供了一個(gè)添加NSTimer的方法,這個(gè)方法是在應(yīng)用正常狀態(tài)下會(huì)回調(diào)贡珊。

runloop的mode作用是什么最爬?

mode主要是用來指定事件在運(yùn)行循環(huán)中的優(yōu)先級(jí)的,分為:
?   NSDefaultRunLoopMode(kCFRunLoopDefaultMode):默認(rèn)门岔,空閑狀態(tài)
?   UITrackingRunLoopMode:ScrollView滑動(dòng)時(shí)會(huì)切換到該Mode
?   UIInitializationRunLoopMode:run loop啟動(dòng)時(shí)爱致,會(huì)切換到該mode
?   NSRunLoopCommonModes(kCFRunLoopCommonModes):Mode集合
蘋果公開提供的Mode有兩個(gè):
?   NSDefaultRunLoopMode(kCFRunLoopDefaultMode)
?   NSRunLoopCommonModes(kCFRunLoopCommonModes)
如果我們把一個(gè)NSTimer對(duì)象以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主運(yùn)行循環(huán)中的時(shí)候, ScrollView滾動(dòng)過程中會(huì)因?yàn)閙ode的切換,而導(dǎo)致NSTimer將不再被調(diào)度寒随。當(dāng)我們滾動(dòng)的時(shí)候糠悯,也希望不調(diào)度,那就應(yīng)該使用默認(rèn)模式妻往。但是互艾,如果希望在滾動(dòng)時(shí),定時(shí)器也要回調(diào)讯泣,那就應(yīng)該使用common mode忘朝。

請(qǐng)寫出NSTimer使用時(shí)的注意事項(xiàng)(兩項(xiàng)即可)

思路和上一題一樣,如果想要銷毀timer判帮,則必須先將timer置為失效局嘁,否則timer就一直占用內(nèi)存而不會(huì)釋放。造成邏輯上的內(nèi)存泄漏晦墙。該泄漏不能用xcode及instruments測(cè)出來悦昵。 另外對(duì)于要求必須銷毀timer的邏輯處理,未將timer置為失效晌畅,若每次都創(chuàng)建一次但指,則之前的不能得到釋放,則會(huì)同時(shí)存在多個(gè)timer的實(shí)例在內(nèi)存中抗楔。
參考答案:
?   注意timer添加到runloop時(shí)應(yīng)該設(shè)置為什么mode
?   注意timer在不需要時(shí)棋凳,一定要調(diào)用invalidate方法使定時(shí)器失效,否則得不到釋放

UITableViewCell上有個(gè)UILabel连躏,顯示NSTimer實(shí)現(xiàn)的秒表時(shí)間剩岳,手指滾動(dòng)cell過程中,label是否刷新入热,為什么拍棕?

和上一題一樣的思路晓铆,如果要cell滾動(dòng)過程中定時(shí)器正常回調(diào)绰播,UI正常刷新骄噪,那么要將timer放入到CommonModes下,因?yàn)槭荖SDefaultRunLoopMode蠢箩,只有在空閑狀態(tài)下才會(huì)回調(diào)链蕊。

為什么 UIScrollView 的滾動(dòng)會(huì)導(dǎo)致 NSTimer 失效?

  • 思路和上一題一樣谬泌,解決辦法有2個(gè),一個(gè)是更改mode為NSRunLoopCommonModes(無論runloop運(yùn)行在哪個(gè)mode,都能運(yùn)行),還有種辦法是切換到主線程來更新UI界面的刷新
 //將timer添加到NSDefaultRunLoopMode中
 [NSTimer scheduledTimerWithTimeInterval: target: selector:@selector(timerTick:) userInfo: repeats:];
  //然后再添加到NSRunLoopCommonModes里
   NSTimer *timer = [NSTimer timerWithTimeInterval: target: selector:@selector(timerTick:) userInfo: repeats:];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

在滑動(dòng)頁面上的列表時(shí)滔韵,timer會(huì)暫定回調(diào),為什么呵萨?如何解決奏属?

  • 思路和上一題一樣

在開發(fā)中如何使用RunLoop跨跨?什么應(yīng)用場景潮峦?

  • 開啟一個(gè)常駐線程(讓一個(gè)子線程不進(jìn)入消亡狀態(tài),等待其他線程發(fā)來消息勇婴,處理其他事件)

  • 在子線程中開啟一個(gè)定時(shí)器

  • 在子線程中進(jìn)行一些長期監(jiān)控

  • 可以控制定時(shí)器在特定模式下執(zhí)行

  • 可以讓某些事件(行為忱嘹、任務(wù))在特定模式下執(zhí)行

  • 可以添加Observer監(jiān)聽RunLoop的狀態(tài),比如監(jiān)聽點(diǎn)擊事件的處理(在所有點(diǎn)擊事件之前做一些事情)

文章如有問題耕渴,請(qǐng)留言拘悦,我將及時(shí)更正。

滿地打滾賣萌求贊橱脸,如果本文幫助到你础米,輕點(diǎn)下方的紅心,給作者君增加更新的動(dòng)力添诉。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末屁桑,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子栏赴,更是在濱河造成了極大的恐慌蘑斧,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,843評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件须眷,死亡現(xiàn)場離奇詭異竖瘾,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)花颗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門捕传,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人扩劝,你說我怎么就攤上這事乐横∏箝希” “怎么了?”我有些...
    開封第一講書人閱讀 163,187評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵葡公,是天一觀的道長罐农。 經(jīng)常有香客問我,道長催什,這世上最難降的妖魔是什么涵亏? 我笑而不...
    開封第一講書人閱讀 58,264評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮蒲凶,結(jié)果婚禮上气筋,老公的妹妹穿的比我還像新娘。我一直安慰自己旋圆,他們只是感情好宠默,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評(píng)論 6 390
  • 文/花漫 我一把揭開白布灵巧。 她就那樣靜靜地躺著,像睡著了一般刻肄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上敏弃,一...
    開封第一講書人閱讀 51,231評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音绿饵,去河邊找鬼瓶颠。 笑死拟赊,一個(gè)胖子當(dāng)著我的面吹牛步清,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播欢搜,決...
    沈念sama閱讀 40,116評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼谴轮,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了第步?” 一聲冷哼從身側(cè)響起缘琅,我...
    開封第一講書人閱讀 38,945評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤廓推,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后樊展,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,367評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡雷酪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評(píng)論 2 333
  • 正文 我和宋清朗相戀三年涝婉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了哥力。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片墩弯。...
    茶點(diǎn)故事閱讀 39,754評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖最住,靈堂內(nèi)的尸體忽然破棺而出怠惶,到底是詐尸還是另有隱情涨缚,我是刑警寧澤策治,帶...
    沈念sama閱讀 35,458評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站通惫,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏履腋。R本人自食惡果不足惜珊燎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評(píng)論 3 327
  • 文/蒙蒙 一悔政、第九天 我趴在偏房一處隱蔽的房頂上張望延旧。 院中可真熱鬧谋国,春花似錦迁沫、人聲如沸捌蚊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽溺拱。三九已至,卻和暖如春迫摔,著一層夾襖步出監(jiān)牢的瞬間泥从,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評(píng)論 1 269
  • 我被黑心中介騙來泰國打工躯嫉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人祈餐。 一個(gè)月前我還...
    沈念sama閱讀 47,797評(píng)論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像帆阳,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蜒谤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評(píng)論 2 354

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