[iOS面試]第7章 RunLoop相關(guān)面試問題

本文主講RunLoop相關(guān)面試問題并蝗,包括RunLoop概念祭犯、數(shù)據(jù)結(jié)構(gòu)秸妥、事件循環(huán)機(jī)制、RunLoop與NSTimer沃粗、RunLoop與多線程粥惧。

一、RunLoop概念

1最盅、 問題:什么是RunLoop?

答:RunLoop是通過內(nèi)部維護(hù)的事件循環(huán)來對事件/消息進(jìn)行管理的一個(gè)對象突雪。
1、沒有消息需要處理時(shí)涡贱,休眠以避免資源占有咏删。
2、有消息需要處理時(shí)问词,立刻被喚醒督函。

2、EventLoop

沒有消息需要被處理時(shí), 系統(tǒng)會(huì)將當(dāng)前線程所有權(quán)轉(zhuǎn)化為內(nèi)核態(tài), 當(dāng)有消息需要處理時(shí), 系統(tǒng)會(huì)將當(dāng)前線程的狀態(tài)切換回用戶態(tài).
所以RunLoop的循環(huán)并不是一個(gè)單純的死循環(huán), 而是通過狀態(tài)切換, 達(dá)到?jīng)]有消息時(shí)休眠, 有消息時(shí)喚醒的這樣一個(gè)事件循環(huán)機(jī)制.

EventLoop
在沒有消息處理時(shí)戏售,休眠以避免資源占用侨核,它的狀態(tài)切換是怎么樣的?
  • 沒有消息處理 是從用戶態(tài)通過系統(tǒng)調(diào)用進(jìn)入內(nèi)核態(tài).
    也就是當(dāng)沒有消息要處理時(shí),進(jìn)程或者說線程會(huì)進(jìn)入一個(gè)休眠狀態(tài)灌灾,而休眠狀態(tài)的一個(gè)過渡相當(dāng)于是把當(dāng)前線程的控制權(quán)轉(zhuǎn)移給了內(nèi)核態(tài)

  • 當(dāng)有消息需要處理時(shí)就會(huì)被立刻喚醒
    實(shí)際上就是由內(nèi)核態(tài)用戶態(tài)的一個(gè)狀態(tài)切換

用戶態(tài)和內(nèi)核態(tài)的介紹:
  • 應(yīng)用程序一般都是運(yùn)行在用戶態(tài)上面搓译,也就是用戶進(jìn)程包括開發(fā)所使用的絕大多數(shù)的 API 都是針對于用戶層面的.
  • 而當(dāng)發(fā)生了系統(tǒng)調(diào)用,需要使用一些關(guān)于操作系統(tǒng)锋喜,以及一些底層內(nèi)核相關(guān)的一些指令或者 API 的話些己,就觸發(fā)了系統(tǒng)調(diào)用,而有些系統(tǒng)調(diào)用就會(huì)發(fā)生狀態(tài)空間的切換嘿般,這種切換空間或者說之所以區(qū)分用戶態(tài)內(nèi)核態(tài)實(shí)際上是對·計(jì)算機(jī)的一些資源調(diào)度·段标,包括資源管理進(jìn)行一個(gè)統(tǒng)一或者說一致性的操作,這樣的話就可以合理的安排資源調(diào)度炉奴,包括可以避免一些特殊的異常
  • 比如說在內(nèi)核態(tài)往往有一些線性指令逼庞,中斷,包括一些開機(jī)關(guān)機(jī)的一些操作瞻赶,如果說每一個(gè)用戶進(jìn)程可以假想是一個(gè) app赛糟,每一個(gè) app 都可以促使當(dāng)前用戶手機(jī)關(guān)機(jī)或者說中斷,這種場景是無法想象的砸逊,所以要有一個(gè)用戶態(tài)到內(nèi)核態(tài)上面的一個(gè)區(qū)分璧南,同時(shí)內(nèi)核態(tài)里面的一些內(nèi)容可以對用戶態(tài)當(dāng)中的一些線程進(jìn)行調(diào)度和管理包括進(jìn)程間的一些通信
問題:main函數(shù)為什么能保證一直運(yùn)行狀態(tài)不退出?

答:

  • 在main函數(shù)中調(diào)用UIApplicationMain()函數(shù), 這個(gè)函數(shù)內(nèi)部會(huì)啟動(dòng)一個(gè)主線程的RunLoop
  • RunLoop是對事件循環(huán)的維護(hù)機(jī)制, 可以不斷的接收消息,比如說點(diǎn)擊屏幕的事件师逸,滑動(dòng)列表司倚,及處理網(wǎng)絡(luò)請求的返回,那么接收消息之后對這個(gè)事件進(jìn)行處理,處理完之后就會(huì)再進(jìn)行等待, 通過用戶態(tài)到內(nèi)核態(tài)的切換, 從而避免資源占用, 讓當(dāng)前線程處于休眠狀態(tài).
  • 注意:等待 != 死循環(huán) 动知,RunLoop的循環(huán)通過狀態(tài)切換, 達(dá)到?jīng)]有消息時(shí)休眠,用戶態(tài)切換到內(nèi)核態(tài),有消息時(shí)喚醒,內(nèi)核態(tài)切換到用戶態(tài)的這樣一個(gè)事件循環(huán)機(jī)制
什么是事件循環(huán)皿伺,事件循環(huán)的機(jī)制是怎樣的?
  • 維護(hù)的事件循環(huán)可以用來不斷的處理消息或者說事件盒粮,對他們進(jìn)行管理
  • 同時(shí)當(dāng)沒有消息需要管理時(shí)用從用戶態(tài)切換到內(nèi)核態(tài)心傀,由此可以用來進(jìn)行當(dāng)前線程的休眠,然后避免資源占用
  • 同時(shí)當(dāng)有消息需要處理時(shí)拆讯,會(huì)發(fā)生從內(nèi)核態(tài)到用戶態(tài)的切換,然后當(dāng)前的用戶線程會(huì)被喚醒
  • 所以狀態(tài)的切換才是 RunLoop 的關(guān)鍵點(diǎn)

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

RunLoop開源代碼地址
在 OC 中實(shí)際提供了兩個(gè) RunLoop 的种呐。
一個(gè)是 NSRunLoop,一個(gè)是 CFRunLoop弃甥。
NSRunLoop 是對 CFRunLoop 的封裝爽室,提供了一些面向?qū)ο蟮?API。
NSRunLoop 是位于 Foundation 當(dāng)中的淆攻,CFRunLoop 位于 CoreFoundation 當(dāng)中的阔墩。

RunLoop 的數(shù)據(jù)結(jié)構(gòu)主要有三個(gè):

  • CFRunLoop
  • CFRunLoopMode
  • Source/Timer/Observer

1、CFRunLoop

  • CFRunLoop數(shù)據(jù)結(jié)構(gòu) 由五部分組成: pthread 瓶珊、currentMode啸箫、modes、commonModes伞芹、commonModeItems


    CFRunLoop數(shù)據(jù)結(jié)構(gòu)
//源碼
struct __CFRunLoop {
    CFMutableSetRef _commonModes;     // Set
    CFMutableSetRef _commonModeItems; // Set
    CFRunLoopModeRef _currentMode;    // Current Runloop Mode
    CFMutableSetRef _modes;           // Set
    ...
};
  • (1)忘苛、pthread:一一對應(yīng)(RunLoop和線程的關(guān)系)
  • (2)、currentMode:CFRunLoopMode
  • (3)唱较、modes:NSMutableSet<CFRunLoopMode*>
  • (4)扎唾、commonModes:NSMutableSet<NSString*>
    1>、commonMode不是實(shí)際存在的一種Mode南缓。
    2>胸遇、是同步Source/Timer/Observer到多個(gè)Mode中的一種技術(shù)方案。
  • (5)汉形、commonModeItems:包含Observer纸镊、Timer、Source

2获雕、CFRunLoopMode數(shù)據(jù)結(jié)構(gòu)

CFRunLoopMode數(shù)據(jù)結(jié)構(gòu)
//源碼
struct __CFRunLoopMode {
    CFStringRef _name;            // Mode Name, 例如 @"kCFRunLoopDefaultMode"
    CFMutableSetRef _sources0;    // Set
    CFMutableSetRef _sources1;    // Set
    CFMutableArrayRef _observers; // Array
    CFMutableArrayRef _timers;    // Array
    ...
};
  • name: 對應(yīng)某一個(gè)runloopMode名稱(如 NSDefaultRunLoopMode)
  • sources0: MutableSet 集合類型數(shù)據(jù)結(jié)構(gòu)
  • sources1: MutableSet 集合類型數(shù)據(jù)結(jié)構(gòu)
  • observers: MutableArray
  • timers: MutableArray

3薄腻、Source/Timer/Observer

  • 在 CF 框架當(dāng)中官方名稱叫 CFRunLoopSource ,有兩種 source0 和 source1
    喚醒線程就是從內(nèi)核態(tài)切換到用戶態(tài)
1>届案、CFRunLoopSource

source0:需要手動(dòng)喚醒線程庵楷。
source1:具備喚醒線程的能力。

source0: 非系統(tǒng)事件.
只包含了一個(gè)回調(diào)(函數(shù)指針),它并不能主動(dòng)觸發(fā)事件尽纽。使用時(shí)咐蚯,你需要先調(diào)用 CFRunLoopSourceSignal(source),將這個(gè) Source 標(biāo)記為待處理弄贿,然后手動(dòng)調(diào)用 CFRunLoopWakeUp(runloop) 來喚醒 RunLoop春锋,讓其處理這個(gè)事件。

source1 : 系統(tǒng)事件
包含了一個(gè) mach_port和一個(gè)回調(diào)(函數(shù)指針)差凹,被用于通過內(nèi)核和其他線程相互發(fā)送消息期奔。這種 Source 能主動(dòng)喚醒 RunLoop 的線程

2>、CFRunLoopTimer

基于事件的定時(shí)器危尿,和NSTimer是toll-free bridged的呐萌。和平時(shí)所使用的 NSTimer 是具備免費(fèi)橋轉(zhuǎn)換的

3>、CFRunLoopObserver

某個(gè)observer可以監(jiān)聽runloop的狀態(tài)變化谊娇,并作出一定反應(yīng)肺孤。

觀測時(shí)間點(diǎn)

  • KCFRunLoopEntry (RunLoop入口時(shí)機(jī))
  • KCFRunLoopBeforeTimers (通知觀察者RunLoop將要對timer一些相關(guān)事件進(jìn)行處理)
  • KCFRunLoopBeforeSources (將要對處理一些sources事件)
  • KCFRunLoopBeforeWaiting (通知觀察者RunLoop將要進(jìn)入休眠狀態(tài), 即將要發(fā)用戶態(tài)到內(nèi)核態(tài)切換)
  • KCFRunLoopAfterWaiting (內(nèi)核態(tài)到用戶態(tài)切換不久)
  • KCFRunLoopExit (RunLoop退出通知)

可以通過注冊一些 Observer 來實(shí)現(xiàn)對 RunLoop 的一些相關(guān)時(shí)間點(diǎn)的監(jiān)測或者觀察。

4济欢、各個(gè)數(shù)據(jù)結(jié)構(gòu)之間關(guān)系

數(shù)據(jù)結(jié)構(gòu)之間關(guān)系

問題:RunLoop 和Mode以及 Mode和其對應(yīng)的 Source ,Timer , Observer 有什么關(guān)系?
答:

  • 一個(gè)runLoop可以有多個(gè)mode, 一對多關(guān)系
  • mode 和Source, Timer, Observer 是一對多的關(guān)系
    可以看到, 一個(gè)RunLoop可以有多個(gè)Mode, 而每個(gè)Mode中又可以存放多個(gè)不同的事件, 我們在切換Mode時(shí), 其他Mode的事件將不會(huì)被響應(yīng).

5赠堵、RunLoop的Mode

image.png

Runloop Mode 實(shí)際上是 Source,Timer 和 Observer 的集合法褥,不同的 Mode 把不同組的 Source茫叭,Timer 和 Observer 隔絕開來。Runloop 在某個(gè)時(shí)刻只能跑在一個(gè) Mode 下,處理這一個(gè) Mode 當(dāng)中的 Source,Timer 和 Observer典唇。

蘋果文檔中提到的 Mode 有五個(gè)膨俐,分別是:

  • NSDefaultRunLoopMode:默認(rèn)的mode,正常情況下都是在這個(gè)mode
  • NSConnectionReplyMode
  • NSModalPanelRunLoopMode
  • NSEventTrackingRunLoopMode:使用這個(gè)Mode去跟蹤來自用戶交互的事件(比如UITableView上下滑動(dòng))
  • NSRunLoopCommonModes

iOS 中公開暴露出來的只有 NSDefaultRunLoopModeNSRunLoopCommonModes。 NSRunLoopCommonModes 實(shí)際上是一個(gè) Mode 的集合,默認(rèn)包括 NSDefaultRunLoopMode 和 NSEventTrackingRunLoopMode。

問題:RunLoop為什么會(huì)有多個(gè)mode?
答:

6烁登、CommonModes的特殊性

NSRunLoopCommonModes
(問題:CommonModes是否使用過, 你對CommonModes事怎樣理解?)

  • CommonModes不是實(shí)際存在的一種Mode
  • 是同步Source/Timer/Observer到多個(gè)Mode中的一種技術(shù)方案

在 OC 當(dāng)中經(jīng)常會(huì)通過 NSRunLoopCommonModes 字符串常量來表達(dá) CommonMode。

  • CommonModes實(shí)現(xiàn):一個(gè) Mode 可以將自己標(biāo)記為”Common”屬性(通過將其 ModeName 添加到 RunLoop 的 “commonModes” 中)蔚舀。每當(dāng) RunLoop 的內(nèi)容發(fā)生變化時(shí)饵沧,RunLoop 都會(huì)自動(dòng)將 _commonModeItems 里的 Source/Observer/Timer 同步到具有 “Common” 標(biāo)記的所有Mode里。

三赌躺、RunLoop事件循環(huán)機(jī)制的實(shí)現(xiàn)

在開發(fā)過程中調(diào)用的 NSRunLoop 的 run 系列的相關(guān)方法以及 CFRunLoop 的相關(guān)的 run 方法最終都會(huì)調(diào)用到 CFRunLoopRun() 函數(shù)

1 事件循環(huán)的整體邏輯:

1)在 RunLoop 啟動(dòng)之后首先會(huì)發(fā)出一條通知來告訴觀察者當(dāng)前 RunLoop 即將啟動(dòng)
2)之后 RunLoop 將要處理 Timer/Sources0 事件狼牺,發(fā)出通知
3)進(jìn)入正式 Sources0 的處理
4)如果有 Sources1 需要處理,這個(gè)時(shí)候會(huì)用過一條 goto 語句來進(jìn)行代碼邏輯的跳轉(zhuǎn)礼患,來處理喚醒時(shí)收到的消息
5)如果沒有 Source1要處理是钥,此時(shí)線程將要休眠掠归,同時(shí)也會(huì)發(fā)送通知給 Observe,然后就發(fā)生了用戶態(tài)到內(nèi)核態(tài)的切換
6)線程正式進(jìn)入休眠悄泥,等待喚醒
7)之后線程被喚醒虏冻,也要發(fā)送一個(gè)通知,通知觀察著說當(dāng)前線程被喚醒了弹囚,然后處理喚醒時(shí)受到的消息厨相,之后又會(huì)回到第二步

2 當(dāng)處于休眠的runloop,可以通過哪些方式喚醒?

答:

  • 通過 Sources1 進(jìn)行當(dāng)前 RunLoop 的喚醒
  • Timer 事件的回調(diào)
  • 外部手動(dòng)的喚醒

3 App 從啟動(dòng)到退出鸥鹉,這個(gè)過程當(dāng)中系統(tǒng)都發(fā)生了什么蛮穿?

答:

  • 調(diào)用了 main 函數(shù)之后,在 main 函數(shù)中會(huì)調(diào)用 UIApplicationMain 函數(shù)毁渗,在這個(gè)函數(shù)內(nèi)部會(huì)啟動(dòng)一個(gè)主線程的 RunLoop绪撵,然后經(jīng)過一系列的處理最終主線程的 RunLoop 處于休眠狀態(tài),
  • 如果說此時(shí)點(diǎn)擊一個(gè)屏幕祝蝠,會(huì)產(chǎn)生一個(gè) Responder ,然后基于 Responder 最終會(huì)轉(zhuǎn)成 Sources1幻碱,可以把主線程喚醒绎狭,運(yùn)行然后處理,
  • 之后當(dāng)把程序殺死的時(shí)候 RunLoop 就會(huì)退出褥傍,這個(gè)時(shí)候就會(huì)發(fā)出一個(gè)通知即將退出 RunLoop 儡嘶,RunLoop 退出之后線程也就銷毀掉了。

4 RunLoop的核心

main 函數(shù)經(jīng)過一系列的處理之后恍风,內(nèi)部最終會(huì)調(diào)用一個(gè)系統(tǒng)函數(shù) mach_msg() ,于是就發(fā)生了一個(gè)系統(tǒng)調(diào)用蹦狂,經(jīng)過系統(tǒng)調(diào)用當(dāng)前用戶線程就把控制權(quán)轉(zhuǎn)交核心態(tài),然后 mach_msg() 在一定條件下會(huì)返回給調(diào)用方朋贬,觸發(fā)返回的邏輯就是喚醒線程的邏輯凯楔,比如收到了一個(gè) Sources1 或者 Timer 事件的回調(diào),包括外部手動(dòng)喚醒锦募,就可以觸發(fā)核心態(tài)到用戶態(tài)的切換摆屯,那么當(dāng)前app的主線程循環(huán)就會(huì)被喚醒,這就是 RunLoop 的核心

四糠亩、RunLoop與NSTimer

問題:滑動(dòng)TableView的時(shí)候虐骑,我們的定時(shí)器還會(huì)生效嗎?
答:

  • Runloop Mode 實(shí)際上是 Source赎线,Timer 和 Observer 的集合廷没,不同的 Mode 把不同組的 Source,Timer 和 Observer 隔絕開來垂寥。Runloop 在某個(gè)時(shí)刻只能跑在一個(gè) Mode 下颠黎,處理這一個(gè) Mode 當(dāng)中的 Source另锋,Timer 和 Observer。

  • 滑動(dòng)TableView的時(shí)候Mode發(fā)生切換盏缤,從KCFRunLoopDefaultMode切換到UITrackingRunLoopMode. (當(dāng)把Source/Timer/Observer 添加到某個(gè)Mode上, 如果當(dāng)前runloop運(yùn)行在另一個(gè)mode上, 對應(yīng)Source/Timer/Observer是沒有辦法進(jìn)行后續(xù)處理和回調(diào).)

  • 解決: 通過函數(shù)CFRunLoopAddTimer() 將timer添加到commonMode上.

  • CommonMode不是實(shí)際存在的一種Mode, 只是將一些mode打上common標(biāo)記, 然后可以把某個(gè)事件源(如Timer)同步到多個(gè)mode中.

void CFRunLoopAddTimer(runloop, timer, commonMode)

五砰蠢、RunLoop與多線程

1、怎樣實(shí)現(xiàn)一個(gè)常駐線程?

(1)唉铜、為當(dāng)前線程開啟一個(gè)RunLoop台舱。
(2)、向該RunLoop中添加一個(gè)Port/Source等維持RunLoop的事件循環(huán)潭流。
(3)竞惋、啟動(dòng)該RunLoop。

實(shí)現(xiàn)一個(gè)常駐線程的基本步驟:

  • 1)為當(dāng)前線程開啟一個(gè) RunLoop :NSRunLoop *runLoop = [NSRunLoop currentRunLoop];灰嫉,因?yàn)楂@取當(dāng)前 RunLoop 這個(gè)方法本身會(huì)查找如果當(dāng)前線程沒有 RunLoop 的話拆宛,會(huì)在系統(tǒng)的內(nèi)部創(chuàng)建
  • 2)如果線程沒有資源或者事件源要處理的話,默認(rèn)情況下是不能維持事件循環(huán)的就會(huì)直接退出了讼撒,所以需要給他添加一個(gè) Port/Source 來維持他的時(shí)間循環(huán)機(jī)制
  • 3)然后再調(diào)用 RunLoop 的 run 方法就可以實(shí)現(xiàn)一個(gè)常駐線程

2浑厚、RunLoop和線程關(guān)系

(1)、runloop與線程是一一對應(yīng)的根盒,一個(gè)runloop對應(yīng)一個(gè)核心的線程钳幅,為什么說是核心的,是因?yàn)閞unloop是可以嵌套的炎滞,但是核心的只能有一個(gè)敢艰,他們的關(guān)系保存在一個(gè)全局的字典里。
(2)册赛、runloop是來管理線程的钠导,當(dāng)線程的runloop被開啟后,線程會(huì)在執(zhí)行完任務(wù)后進(jìn)入休眠狀態(tài)森瘪,有了任務(wù)就會(huì)被喚醒去執(zhí)行任務(wù)牡属。
(3)、runloop在第一次獲取時(shí)被創(chuàng)建扼睬,在線程結(jié)束時(shí)被銷毀湃望。
(4)、對于主線程來說痰驱,runloop在程序一啟動(dòng)就默認(rèn)創(chuàng)建好了证芭。
(5)、對于子線程來說担映,runloop是懶加載的废士,只有當(dāng)我們使用的時(shí)候才會(huì)創(chuàng)建。

    dispatch_queue_t queue = dispatch_queue_create("com.codeTao.testQueue", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        [self performSelector:@selector(timerAction) withObject:nil afterDelay:1];
        [[NSRunLoop currentRunLoop] run];
    });

(6)蝇完、子線程中使用定時(shí)器官硝,需將定時(shí)器添加至RunLoop中矗蕊,確保子線程的runloop被創(chuàng)建,不然定時(shí)器不會(huì)回調(diào)氢架。

  dispatch_queue_t queue = dispatch_queue_create("com.codeTao.testQueue", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        //此種方式創(chuàng)建的timer已經(jīng)添加到NSRunloop中了
        NSTimer *timer1 =[NSTimer scheduledTimerWithTimeInterval:0 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop] run];

        //此種方式創(chuàng)建的timer沒有添加至runloop中
        NSTimer *timer2 = [NSTimer timerWithTimeInterval:1.0f target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop] addTimer:timer2 forMode:NSDefaultRunLoopMode];
        [[NSRunLoop currentRunLoop] run];
    });

六 RunLoop面試總結(jié)

問題:什么是RunLoop? 它是怎樣做到有事做事,沒事休息?

答: RunLoop是通過內(nèi)部維護(hù)的事件循環(huán)來對事件/消息進(jìn)行管理的一個(gè)對象傻咖。
調(diào)用 [CFRunLoop run] 相關(guān)方法后, 會(huì)調(diào)用系統(tǒng)函數(shù)mach_msg, 同時(shí)發(fā)生了用戶態(tài)向核心態(tài)的切換,當(dāng)前線程處于休眠狀態(tài).所有做到有事做事,沒事休息

問題:RunLoop與線程是怎樣的關(guān)系?

答:

  • runloop與線程是一一對應(yīng)的
  • 一個(gè)線程默認(rèn)是沒有runloop的, 需要手動(dòng)創(chuàng)建

問題:如何實(shí)現(xiàn)一個(gè)常駐線程?

答:三個(gè)步驟:
(1)、創(chuàng)建一個(gè)線程對應(yīng)的RunLoop岖研。
(2)卿操、向該RunLoop中添加一個(gè)Port/Source/Timer/Observer等維持RunLoop的事件循環(huán)。
(3)孙援、啟動(dòng)該RunLoop 害淤。調(diào)用CFRunLoop run方法
注意:運(yùn)行的模式和添加模式必須是同一個(gè),否則外部使用while循環(huán)會(huì)導(dǎo)致死循環(huán)

問題:怎樣保證子線程數(shù)據(jù)回來更新UI的時(shí)候不打斷用戶的滑動(dòng)操作?

答:

  • 用戶進(jìn)行滑動(dòng)過程中,當(dāng)前RunLoop運(yùn)行在UITrackingRunLoopMode下
  • 我們一般在子線程中進(jìn)行網(wǎng)絡(luò)請求, 所以可以將子線程拋給主線程數(shù)據(jù)并進(jìn)行UI更新的邏輯封裝起來提交到主線程的NSDefaultRunLoopMode下.
  • 這樣拋回來的任務(wù),當(dāng)用戶滑動(dòng)時(shí)處于UITrackingRunLoopMode下就不會(huì)執(zhí)行任務(wù). 當(dāng)手停止滑動(dòng)操作后, 當(dāng)前線程mode切換到NSDefaultRunLoopMode下,再處理子線程上拋給主線程的任務(wù),這樣就不會(huì)打斷用戶滑動(dòng)操作.

問題:我們可以監(jiān)聽 RunLoop 哪些時(shí)間點(diǎn)?

答:

  • KCFRunLoopEntryRunLoop 的入口時(shí)機(jī)拓售,當(dāng) RunLoop 準(zhǔn)備啟動(dòng)的時(shí)候系統(tǒng)會(huì)給我們一個(gè)回調(diào)通知窥摄,這個(gè)通知掉 CFRunLoopEntry
  • KCFRunLoopBeforeTimers代表的含義:通知觀察者 RunLoop 將要對 Timer 一些相關(guān)事件進(jìn)行處理了
  • KCFRunLoopBeforeSources代表將要處理一些 Source 事件
  • KCFRunLoopBeforeWaiting通知對應(yīng)觀察者,當(dāng)前 RunLoop 將要進(jìn)入休眠狀態(tài)础淤,這個(gè)通知或者說觀測點(diǎn)是非常重要的一個(gè)觀測點(diǎn)崭放,在 RunLoop 發(fā)送這個(gè)通知的時(shí)候,即將要發(fā)生用戶態(tài)到內(nèi)核態(tài)的切換
  • KCFRunLoopAfterWaiting這也是一個(gè)重要的觀測點(diǎn)鸽凶,這個(gè)通知發(fā)出的時(shí)機(jī)恰好是從內(nèi)核態(tài)切換到用戶態(tài)之后的不久之間
  • KCFRunLoopExit代表 RunLoop 退出的通知

問題:什么時(shí)候使用Runloop?
當(dāng)需要和該線程進(jìn)行交互的時(shí)候才會(huì)使用Runloop

問題: Runloop和線程是什么關(guān)系币砂?
答:

  • 每條線程都有唯一的一個(gè)與之對應(yīng)的RunLoop對象,其關(guān)系是保存在一個(gè)全局的 Dictionary 里吱瘩;
  • 主線程的RunLoop已經(jīng)自動(dòng)創(chuàng)建,子線程的RunLoop需要手動(dòng)創(chuàng)建迹缀;
  • RunLoop在第一次獲取時(shí)創(chuàng)建使碾,在線程結(jié)束時(shí)銷毀

問題:Runloop的mode作用是什么?
答:
指定事件在運(yùn)行循環(huán)中的優(yōu)先級的祝懂,
線程的運(yùn)行需要不同的模式票摇,去響應(yīng)各種不同的事件,去處理不同情境模式砚蓬。(比如可以優(yōu)化tableview的時(shí)候可以設(shè)置UITrackingRunLoopMode下不進(jìn)行一些操作矢门,比如設(shè)置圖片等。)

問題: 以+scheduledTimerWithTimeInterval:的方式觸發(fā)的timer灰蛙,在滑動(dòng)頁面上的列表時(shí)祟剔,timer會(huì)暫停回調(diào)摩梧, 為什么物延?
答:
滑動(dòng)scrollView時(shí),主線程的RunLoop會(huì)切換到UITrackingRunLoopMode這個(gè)Mode仅父,執(zhí)行的也是UITrackingRunLoopMode下的任務(wù)(Mode中的item)叛薯,而timer是添加在NSDefaultRunLoopMode下的浑吟,所以timer任務(wù)并不會(huì)執(zhí)行,只有當(dāng)UITrackingRunLoopMode的任務(wù)執(zhí)行完畢耗溜,runloop切換到NSDefaultRunLoopMode后组力,才會(huì)繼續(xù)執(zhí)行timer。

問題: 如何解決在滑動(dòng)頁面上的列表時(shí)抖拴,timer會(huì)暫土亲郑回調(diào)?
答:
Timer放到NSRunLoopCommonModes中執(zhí)行即可

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

問題: NSTimer使用時(shí)需要注意什么城舞?
答:

  • 注意timer添加到runloop時(shí)應(yīng)該設(shè)置為什么mode
  • 注意timer在不需要時(shí)轩触,一定要調(diào)用invalidate方法使定時(shí)器失效,否則得不到釋放

問題: RunLoop 有哪些應(yīng)用家夺?
答: 常駐內(nèi)存脱柱、AutoreleasePool 自動(dòng)釋放池

問題: AutoreleasePool 和 RunLoop 有什么聯(lián)系?
答:
iOS應(yīng)用啟動(dòng)后會(huì)注冊兩個(gè) Observer 管理和維護(hù) AutoreleasePool拉馋。應(yīng)用程序剛剛啟動(dòng)時(shí)默認(rèn)注冊了很多個(gè)Observer榨为,其中有兩個(gè)Observer的 callout 都是 _ wrapRunLoopWithAutoreleasePoolHandler,這兩個(gè)是和自動(dòng)釋放池相關(guān)的兩個(gè)監(jiān)聽煌茴。

  • 第一個(gè) Observer 會(huì)監(jiān)聽 RunLoop 的進(jìn)入随闺,它會(huì)回調(diào)objc_autoreleasePoolPush() 向當(dāng)前的 AutoreleasePoolPage 增加一個(gè)哨兵對象標(biāo)志創(chuàng)建自動(dòng)釋放池。這個(gè) Observer 的 order 是 -2147483647 優(yōu)先級最高蔓腐,確保發(fā)生在所有回調(diào)操作之前矩乐。

  • 第二個(gè) Observer 會(huì)監(jiān)聽 RunLoop 的進(jìn)入休眠和即將退出 RunLoop 兩種狀態(tài),在即將進(jìn)入休眠時(shí)會(huì)調(diào)用 objc_autoreleasePoolPop() 和 objc_autoreleasePoolPush() 根據(jù)情況從最新加入的對象一直往前清理直到遇到哨兵對象回论。而在即將退出 RunLoop 時(shí)會(huì)調(diào)用objc_autoreleasePoolPop() 釋放自動(dòng)自動(dòng)釋放池內(nèi)對象散罕。這個(gè)Observer 的 order 是 2147483647 ,優(yōu)先級最低傀蓉,確保發(fā)生在所有回調(diào)操作之后欧漱。

問題:NSRunLoop 和 CFRunLoopRef 區(qū)別?
答: CFRunLoopRef 基于C 線程安全,NSRunLoop 基于 CFRunLoopRef 面向?qū)ο蟮腁PI 是不安全的

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載葬燎,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者误甚。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市谱净,隨后出現(xiàn)的幾起案子窑邦,更是在濱河造成了極大的恐慌,老刑警劉巖壕探,帶你破解...
    沈念sama閱讀 218,451評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件奕翔,死亡現(xiàn)場離奇詭異,居然都是意外死亡浩蓉,警方通過查閱死者的電腦和手機(jī)派继,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評論 3 394
  • 文/潘曉璐 我一進(jìn)店門宾袜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人驾窟,你說我怎么就攤上這事庆猫。” “怎么了绅络?”我有些...
    開封第一講書人閱讀 164,782評論 0 354
  • 文/不壞的土叔 我叫張陵月培,是天一觀的道長。 經(jīng)常有香客問我恩急,道長杉畜,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,709評論 1 294
  • 正文 為了忘掉前任衷恭,我火速辦了婚禮此叠,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘随珠。我一直安慰自己灭袁,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評論 6 392
  • 文/花漫 我一把揭開白布窗看。 她就那樣靜靜地躺著茸歧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪显沈。 梳的紋絲不亂的頭發(fā)上软瞎,一...
    開封第一講書人閱讀 51,578評論 1 305
  • 那天,我揣著相機(jī)與錄音拉讯,去河邊找鬼涤浇。 笑死,一個(gè)胖子當(dāng)著我的面吹牛遂唧,可吹牛的內(nèi)容都是我干的芙代。 我是一名探鬼主播吊奢,決...
    沈念sama閱讀 40,320評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼盖彭,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了页滚?” 一聲冷哼從身側(cè)響起召边,我...
    開封第一講書人閱讀 39,241評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎裹驰,沒想到半個(gè)月后隧熙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,686評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡幻林,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評論 3 336
  • 正文 我和宋清朗相戀三年贞盯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了音念。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,992評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡躏敢,死狀恐怖闷愤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情件余,我是刑警寧澤讥脐,帶...
    沈念sama閱讀 35,715評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站啼器,受9級特大地震影響旬渠,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜端壳,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評論 3 330
  • 文/蒙蒙 一告丢、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧更哄,春花似錦芋齿、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至麻敌,卻和暖如春栅炒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背术羔。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評論 1 270
  • 我被黑心中介騙來泰國打工赢赊, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人级历。 一個(gè)月前我還...
    沈念sama閱讀 48,173評論 3 370
  • 正文 我出身青樓释移,卻偏偏與公主長得像,于是被迫代替她去往敵國和親寥殖。 傳聞我的和親對象是個(gè)殘疾皇子玩讳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評論 2 355

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