RunLoop學(xué)習(xí)起來是很抽象,也不好理解,所以一定多看幾次,多學(xué)學(xué)才能學(xué)好!這也是中高級iOS必須掌握的知識點,面試中經(jīng)常遇到.
什么是 RunLoop继榆?
Run 表示運(yùn)行,Loop 表示循環(huán)共屈。結(jié)合在一起就是運(yùn)行循環(huán)的意思。RunLoop就是在程序運(yùn)行過程中循環(huán)做一些事情.
RunLoop的應(yīng)用范疇有哪些?
定時器(Timer)、PerformSelector
GCD Async Main Queue
事件響應(yīng)捞稿、手勢識別、界面刷新
網(wǎng)絡(luò)請求
AutoreieasePool
上面這些底層都是RunLoop在支撐,說白了,如果沒有RunLoop支撐,上面的這些都無法實現(xiàn).
如果沒有RunLoop會發(fā)生什么呢?像我們的命令行項目,創(chuàng)建出來默認(rèn)就是沒有RunLoop,請看下圖
因為沒有RunLoop,程序執(zhí)行到第13行的時候,就會自動退出.
而我們iOS項目的main函數(shù)里面都有UIApplicationMain(argc, argv,nil, appDelegateClassName);這個代碼,這里就是創(chuàng)建了一個主線程的RunLoop,所以我們程序不會退出,一直在運(yùn)行中.我們可以大致寫一下main函數(shù)里面的偽代碼如下:
retVal這個等于0,當(dāng)沒有事件處理的時候,RunLoop就會sleep就是類似睡覺,一旦有事件需要處理,比如點擊钝尸、刷新事件等process_message就會去處理這個事件,處理完了繼續(xù)休息,retVal=0,程序就會一直執(zhí)行,不會退出,這就是RunLoop作用.
作為一個開發(fā)者括享,有一個學(xué)習(xí)的氛圍跟一個交流圈子特別重要,這是一個我的iOS開發(fā)交流群:130 595 548珍促,不管你是小白還是大牛都?xì)g迎入駐 铃辖,讓我們一起進(jìn)步,共同發(fā)展V硇稹(群內(nèi)會免費(fèi)提供一些群主收藏的免費(fèi)學(xué)習(xí)書籍資料以及整理好的幾百道面試題和答案文檔=空丁)
RunLoop的基本作用
1.保持程序的持續(xù)運(yùn)行
2.處理App中的各種事件(比如觸摸事件、定時器事件等)
3.節(jié)省了CPU資源,提高程序性能:該做事時做事,該休息時休息
...
獲取RunLoop對象
iOS中有2套API來訪問和使用RunLoop :
Foundation : NSRunLoop (OC語言里面的)
Core Foundation : CFRunLoopRef (C語言里面的)
NSRunLoop和CFRunLoopRef都代表著RunLoop對象
NSRunLoop是基于CFRunLoopRef的一層OC包裝
CFRunLoopRef是開源的.(CFRunLoopRef參考鏈接)
其實我們很多都是由OC包裝出來的,請看下面:
獲取當(dāng)前的RunLoop
獲取當(dāng)前RunLoop和主線程RunLoop
獲取RunLoop
這里注意,地址不一樣,因為NSRunLoop是對CFRunLoopDef做了一層包裝,你可以用OC的NSLog("%@",[NSRunLoop MainRunLoop])獲取對比一下,它的地址就是C語言獲取的地址.主線程只有一個RunLoop.
RunLoop與線程
每條線程都有唯一的一個與之對應(yīng)的RunLoop對象(一一對應(yīng))
RunLoop保存在一個全局的Dictionary里,線程作為key,RunLoop作為value
線程剛創(chuàng)建的時候并沒有RunLoop對象,RunLoop會在第一次獲取它時創(chuàng)建
RunLoop會在線程結(jié)束時銷毀
主線程的RunLoop已經(jīng)自動創(chuàng)建,子線程默認(rèn)沒有開啟RunLoop.
源碼窺探看一下:CFRunLoopGetCurrent
由于源碼不能像objc直接打開,我們把它拉到項目中查看.
從字典也能看出來是一對一的關(guān)系.而且確實是第一次獲取的時候是空的,然后再去創(chuàng)建這個RunLoop.
那我們就繼續(xù)來了解RunLoop內(nèi)部的數(shù)據(jù)結(jié)構(gòu),到底是怎么工作的.
RunLoop相關(guān)的類
Core Foundation中關(guān)于RunLoop的5個類
1.CFRunLoopRef
2.CFRunLoopModeRef
3.CFRunLoopSourceRef
4.CFRunLoopTimerRef
5.CFRunLoopObserverRef
再看下CFRunLoopRef的底層源碼:
就是上面這個結(jié)構(gòu)體,我們用到的可能就是紅色這些.pthread是線程,每個runloop都會保存這個東西.最后面那個_modes,這個是個集合來著,CFMutableSetRef我們能想到我們自己用的set也是一個集合來著,比如NSMutableSet也是一個集合,所以這個_modes里面是存著一堆的mode.
這個mode就是CFRunLoopModeRef類型,所以里面存儲一堆的CFRunLoopModeRef類型的mode.
而_currentMode也是CFRunLoopModeRef這個類型,所以我們很容易得出一個結(jié)論:
一個RunLoop對象里面有一堆的mode,也就是存在_modes里面,里面只有一個是_currentMode.
我們再窺探一下源碼,看下mode里面存儲的是什么?
所以我們來個總結(jié)的圖:
RunLoop有很多種模式,對應(yīng)的_currentMode只有一種.
CFRunLoopModeRef
1.CFRunLoopModeRef它是代表RunLoop的運(yùn)行模式
2.一個RunLoop包含若干個Mode,每個Mode又包含若干個Source0/Source1/Timer/Observer
3.RunLoop啟動時只能選擇其中一個Mode,作為currentMode
4.如果需要切換Mode,只能退出當(dāng)前RunLoop,再重新選擇一個Mode進(jìn)入
5.不同組的Source0/Source1/Timer/Observer能分割開來,互不影響
6.如果Mode里面沒有任何Source0/Source1/Timer/Observer,RunLoop會立馬退出
如果只能在一種模式下運(yùn)行,對性能什么的都有很大好處,比如我在滑動模式下,不考慮不滑動的模式,所以就不會卡頓,順暢很多.還有注意的就是,它切換mode是在循環(huán)里面切換的,所以不會導(dǎo)致程序退出.
常見的mode有2種,其他情況很少見,所以掌握這兩個一般都是沒問題了
1.KCFRunLoopDefaultMode (NSDefaultRunLoopMode):App的默認(rèn)Mode,通常是主線程是在這個Mode下運(yùn)行
2.UITrackingRunLoopMode : 界面跟蹤Mode,用于ScrollView追蹤觸摸滑動,保證界面滑動時不受其他Mode影響
RunLoop到底做哪些事?
RunLoop在不停執(zhí)行的時候到底具體做了哪些事?其實是RunLoop在不停循環(huán)的時候,就是處理每個mode下的Source0穴翩、Source1犬第、Timer、Observer這里面的事件,那我們就來看看這里面具體對應(yīng)的到底是什么事件.
Source0
觸摸事件芒帕、performSelector:onThread:
比如我們的touchbegin這個我們看下下面的代碼:
Source1
基于Port的線程間的通信,系統(tǒng)事件的捕捉.
(兩個線程之間相互傳遞消息的處理,系統(tǒng)事件捕捉,其實也包括觸摸事件,只是把事件捕捉到以后傳遞給Source0).
Timer
NSTimer定時器,performSelector:withObject:afterDelay(這個方法的底層實現(xiàn)也就是NSTimer來實現(xiàn)的)
Observers
用于監(jiān)聽RunLoop的狀態(tài),UI的刷新(BeforeWaiting),Autorelease pool(BeforeWaiting)
(在RunLoop休眠之前都會去執(zhí)行UI的刷新啊歉嗓、Autorelease pool的釋放等)
以上這些東西,完全就是我們平時開發(fā)中經(jīng)常寫的代碼,比如設(shè)置背景色,設(shè)置frame等等.
由于RunLoop知識點比較多,如果寫太多不利于大家的閱讀和消化,所以其他內(nèi)容放在后面介紹!
如果覺得我寫得對您有所幫助,請關(guān)注我背蟆,我會持續(xù)更新??