RunLoop學習起來是很抽象,也不好理解,所以一定多看幾次,多學學才能學好!這也是中高級iOS必須掌握的知識點,面試中經常遇到.
什么是 RunLoop?
Run 表示運行,Loop 表示回圈,結合在一起就是運行回圈的意思,RunLoop就是在程式運行程序中回圈做一些事情.
RunLoop的應用范疇有哪些?
定時器(Timer)、PerformSelector
GCD Async Main Queue
事件回應、手勢識別、界面重繪
網路請求
AutoreieasePool
上面這些底層都是RunLoop在支撐,說白了,如果沒有RunLoop支撐,上面的這些都無法實作.
如果沒有RunLoop會發生什么呢?像我們的命令列專案,創建出來默認就是沒有RunLoop,請看下圖

因為沒有RunLoop,程式執行到第13行的時候,就會自動退出.
而我們iOS專案的main函式里面都有UIApplicationMain(argc, argv,nil, appDelegateClassName);這個代碼,這里就是創建了一個主執行緒的RunLoop,所以我們程式不會退出,一直在運行中.我們可以大致寫一下main函式里面的偽代碼如下:

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

獲取當前的RunLoop
獲取當前RunLoop和主執行緒RunLoop

獲取RunLoop
這里注意,地址不一樣,因為NSRunLoop是對CFRunLoopDef做了一層包裝,你可以用OC的NSLog("%@",[NSRunLoop MainRunLoop])獲取對比一下,它的地址就是C語言獲取的地址.主執行緒只有一個RunLoop.
RunLoop與執行緒
每條執行緒都有唯一的一個與之對應的RunLoop物件(一一對應)
RunLoop保存在一個全域的Dictionary里,執行緒作為key,RunLoop作為value
執行緒剛創建的時候并沒有RunLoop物件,RunLoop會在第一次獲取它時創建
RunLoop會在執行緒結束時銷毀
主執行緒的RunLoop已經自動創建,子執行緒默認沒有開啟RunLoop.

原始碼窺探看一下:CFRunLoopGetCurrent
由于原始碼不能像objc直接打開,我們把它拉到專案中查看.


從字典也能看出來是一對一的關系.而且確實是第一次獲取的時候是空的,然后再去創建這個RunLoop.
那我們就繼續來了解RunLoop內部的資料結構,到底是怎么作業的.
RunLoop相關的類
Core Foundation中關于RunLoop的5個類
1.CFRunLoopRef
2.CFRunLoopModeRef
3.CFRunLoopSourceRef
4.CFRunLoopTimerRef
5.CFRunLoopObserverRef
再看下CFRunLoopRef的底層原始碼:

就是上面這個結構體,我們用到的可能就是紅色這些.pthread是執行緒,每個runloop都會保存這個東西.最后面那個_modes,這個是個集合來著,CFMutableSetRef我們能想到我們自己用的set也是一個集合來著,比如NSMutableSet也是一個集合,所以這個_modes里面是存著一堆的mode.
這個mode就是CFRunLoopModeRef型別,所以里面存盤一堆的CFRunLoopModeRef型別的mode.
而_currentMode也是CFRunLoopModeRef這個型別,所以我們很容易得出一個結論:
一個RunLoop物件里面有一堆的mode,也就是存在_modes里面,里面只有一個是_currentMode.
我們再窺探一下原始碼,看下mode里面存盤的是什么?

所以我們來個總結的圖:

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

Source1
基于Port的執行緒間的通信,系統事件的捕捉.
(兩個執行緒之間相互傳遞訊息的處理,系統事件捕捉,其實也包括觸摸事件,只是把事件捕捉到以后傳遞給Source0).
Timer
NSTimer定時器,performSelector:withObject:afterDelay(這個方法的底層實作也就是NSTimer來實作的)
Observers
用于監聽RunLoop的狀態,UI的重繪(BeforeWaiting),Autorelease pool(BeforeWaiting)
(在RunLoop休眠之前都會去執行UI的重繪啊、Autorelease pool的釋放等)
以上這些東西,完全就是我們平時開發中經常寫的代碼,比如設定背景色,設定frame等等.
由于RunLoop知識點比較多,如果寫太多不利于大家的閱讀和消化,所以其他內容放在后面介紹!
接下來我會繼續介紹Runloop其他知識點.
如果覺得我寫得對您有所幫助,請關注我,我會持續更新😄
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/274088.html
標籤:其他
