RunLoop 學(xué)習(xí)起來是很抽象,也不好理解,所以一定多看幾次,多學(xué)學(xué)才能學(xué)好!這也是中高級 iOS 必須掌握的知識點(diǎn),面試中經(jīng)常遇到
什么是 RunLoop?
Run 表示運(yùn)行,Loop 表示循環(huán)。結(jié)合在一起就是運(yùn)行循環(huán)的意思。RunLoop 就是在程序運(yùn)行過程中循環(huán)做一些事情.
RunLoop 的應(yīng)用范疇有哪些?
定時(shí)器 (Timer)、PerformSelector
GCD Async Main Queue
事件響應(yīng)、手勢識別、界面刷新
網(wǎng)絡(luò)請求
AutoreieasePool
上面這些底層都是 RunLoop 在支撐,說白了,如果沒有 RunLoop 支撐,上面的這些都無法實(shí)現(xiàn)。
如果沒有 RunLoop 會(huì)發(fā)生什么呢?像我們的命令行項(xiàng)目,創(chuàng)建出來默認(rèn)就是沒有 RunLoop,請看下圖

因?yàn)闆]有 RunLoop,程序執(zhí)行到第 13 行的時(shí)候,就會(huì)自動(dòng)退出.
而我們 iOS 項(xiàng)目的 main 函數(shù)里面都有 UIApplicationMain(argc, argv, nil, appDelegateClassName);這個(gè)代碼,這里就是創(chuàng)建了一個(gè)主線程的 RunLoop,所以我們程序不會(huì)退出,一直在運(yùn)行中。我們可以大致寫一下 main 函數(shù)里面的偽代碼如下:

retVal 這個(gè)等于 0,當(dāng)沒有事件處理的時(shí)候,RunLoop 就會(huì) sleep 就是類似睡覺,一旦有事件需要處理,比如點(diǎn)擊、刷新事件等 process_message 就會(huì)去處理這個(gè)事件,處理完了繼續(xù)休息,retVal=0,程序就會(huì)一直執(zhí)行,不會(huì)退出,這就是 RunLoop 作用。
RunLoop 的基本作用
1.保持程序的持續(xù)運(yùn)行
2.處理 App 中的各種事件(比如觸摸事件、定時(shí)器事件等)
3.節(jié)省了 CPU 資源,提高程序性能:該做事時(shí)做事,該休息時(shí)休息
…
獲取 RunLoop 對象
iOS 中有 2 套 API 來訪問和使用 RunLoop :
Foundation : NSRunLoop (OC 語言里面的)
Core Foundation : CFRunLoopRef (C 語言里面的)
NSRunLoop 和 CFRunLoopRef 都代表著 RunLoop 對象
NSRunLoop 是基于 CFRunLoopRef 的一層 OC 包裝
CFRunLoopRef 是開源的.(CFRunLoopRef 參考鏈接)
其實(shí)我們很多都是由 OC 包裝出來的,請看下面:

獲取當(dāng)前的 RunLoop
獲取當(dāng)前 RunLoop 和主線程 RunLoop

獲取 RunLoop
這里注意 “地址不一樣” 因?yàn)?NSRunLoop 是對 CFRunLoopDef 做了一層包裝,你可以用 OC 的 NSLog(“%@”,[NSRunLoop MainRunLoop]) 獲取對比一下,它的地址就是 C 語言獲取的地址。主線程只有一個(gè) RunLoop。
RunLoop 與線程
每條線程都有唯一的一個(gè)與之對應(yīng)的 RunLoop 對象(一一對應(yīng))
RunLoop 保存在一個(gè)全局的 Dictionary 里,線程作為 key,RunLoop 作為 value
線程剛創(chuàng)建的時(shí)候并沒有 RunLoop 對象,RunLoop 會(huì)在第一次獲取它時(shí)創(chuàng)建
RunLoop 會(huì)在線程結(jié)束時(shí)銷毀
主線程的 RunLoop 已經(jīng)自動(dòng)創(chuàng)建,子線程默認(rèn)沒有開啟 RunLoop。

源碼窺探看一下:CFRunLoopGetCurrent
由于源碼不能像 objc 直接打開,我們把它拉到項(xiàng)目中查看。


從字典也能看出來是一對一的關(guān)系。而且確實(shí)是第一次獲取的時(shí)候是空的,然后再去創(chuàng)建這個(gè) RunLoop。
那我們就繼續(xù)來了解 RunLoop 內(nèi)部的數(shù)據(jù)結(jié)構(gòu),到底是怎么工作的。
RunLoop 相關(guān)的類
Core Foundation 中關(guān)于 RunLoop 的 5 個(gè)類
1.CFRunLoopRef
2.CFRunLoopModeRef
3.CFRunLoopSourceRef
4.CFRunLoopTimerRef
5.CFRunLoopObserverRef
再看下 CFRunLoopRef 的底層源碼:

就是上面這個(gè)結(jié)構(gòu)體,我們用到的可能就是紅色這些.pthread 是線程,每個(gè) runloop 都會(huì)保存這個(gè)東西。最后面那個(gè) _modes,這個(gè)是個(gè)集合來著,CFMutableSetRef 我們能想到我們自己用的 set 也是一個(gè)集合來著,比如 NSMutableSet 也是一個(gè)集合,所以這個(gè) _modes 里面是存著一堆的 mode。
這個(gè) mode 就是 CFRunLoopModeRef 類型,所以里面存儲(chǔ)一堆的 CFRunLoopModeRef 類型的 mode。
而 _currentMode 也是 CFRunLoopModeRef 這個(gè)類型,所以我們很容易得出一個(gè)結(jié)論:
一個(gè) RunLoop 對象里面有一堆的 mode,也就是存在 _modes 里面,里面只有一個(gè)是 _currentMode。
我們再窺探一下源碼,看下 mode 里面存儲(chǔ)的是什么?

所以我們來個(gè)總結(jié)的圖:

RunLoop 有很多種模式,對應(yīng)的 _currentMode 只有一種。
CFRunLoopModeRef
1.CFRunLoopModeRef 它是代表 RunLoop 的運(yùn)行模式;
2.一個(gè) RunLoop 包含若干個(gè) Mode,每個(gè) Mode 又包含若干個(gè)
Source0/Source1/Timer/Observer;
3.RunLoop 啟動(dòng)時(shí)只能選擇其中一個(gè) Mode,作為 currentMode;
4.如果需要切換 Mode,只能退出當(dāng)前 RunLoop,再重新選擇一個(gè) Mode 進(jìn)入;
5.不同組的
Source0/Source1/Timer/Observer 能分割開來,互不影響;
6.如果 Mode 里面沒有任何
Source0/Source1/Timer/Observer,RunLoop 會(huì)立馬退出;
如果只能在一種模式下運(yùn)行,對性能什么的都有很大好處,比如我在滑動(dòng)模式下,不考慮不滑動(dòng)的模式,所以就不會(huì)卡頓,順暢很多。還有注意的就是,它切換 mode 是在循環(huán)里面切換的,所以不會(huì)導(dǎo)致程序退出。
常見的 mode 有 2 種,其他情況很少見,所以掌握這兩個(gè)一般都是沒問題了
1.KCFRunLoopDefaultMode (NSDefaultRunLoopMode):App 的默認(rèn) Mode,通常是主線程是在這個(gè) Mode 下運(yùn)行;
2.UITrackingRunLoopMode : 界面跟蹤 Mode,用于 ScrollView 追蹤觸摸滑動(dòng),保證界面滑動(dòng)時(shí)不受其他 Mode 影響;
RunLoop 到底做哪些事?
RunLoop 在不停執(zhí)行的時(shí)候到底具體做了哪些事?其實(shí)是 RunLoop 在不停循環(huán)的時(shí)候,就是處理每個(gè) mode 下的 Source0、Source1、Timer、Observer 這里面的事件,那我們就來看看這里面具體對應(yīng)的到底是什么事件。
Source0
觸摸事件、performSelector:onThread:
比如我們的 touchbegin 這個(gè)我們看下下面的代碼:

Source1
基于 Port 的線程間的通信,系統(tǒng)事件的捕捉。
(兩個(gè)線程之間相互傳遞消息的處理,系統(tǒng)事件捕捉,其實(shí)也包括觸摸事件,只是把事件捕捉到以后傳遞給 Source0)。
Timer
NSTimer 定時(shí)器,
performSelector:withObject:afterDelay (這個(gè)方法的底層實(shí)現(xiàn)也就是 NSTimer 來實(shí)現(xiàn)的)。
Observers
用于監(jiān)聽 RunLoop 的狀態(tài),UI 的刷新 (BeforeWaiting),Autorelease pool(BeforeWaiting)。
(在 RunLoop 休眠之前都會(huì)去執(zhí)行 UI 的刷新啊、Autorelease pool 的釋放等)
以上這些東西,完全就是我們平時(shí)開發(fā)中經(jīng)常寫的代碼,比如設(shè)置背景色,設(shè)置 frame 等等。
由于 RunLoop 知識點(diǎn)比較多,如果寫太多不利于大家的閱讀和消化,所以其他內(nèi)容放在后面介紹!
本文由網(wǎng)上采集發(fā)布,不代表我們立場,轉(zhuǎn)載聯(lián)系作者并注明出處:http://m.zltfw.cn/shbk/37191.html