很多有關(guān)runloop的文章,都是長篇描述竹勉,看懵很多人飞盆,特別是C/C++代碼部分,看完下來次乓,發(fā)生了什么吓歇,我是誰,我在干什么票腰?runloop到底什么城看?
其實(shí),我們看runloop 很簡單杏慰,不為造輪子测柠,不為能鉆研出什么魔法出來
- runloop 能干什么,項(xiàng)目中哪些場景用到了它
- 面試的時(shí)候炼鞠,我怎么去回答,那么長篇大段的原理轰胁,我的天
接下來谒主,跟著我的腳步,揭開runloop的面紗赃阀,先看看面試的時(shí)候需要的理論知識
1.runloop 是什么
- runloop就是循環(huán)來處理程序運(yùn)行過程中出現(xiàn)的各種事件(比如說觸摸事件霎肯、UI刷新事件、定時(shí)器事件榛斯、Selector事件)观游,從而保持程序的持續(xù)運(yùn)行,而在沒有任何任務(wù)處理時(shí)驮俗,會讓線程休眠懂缕。
2.runloop與線程
一條線程對應(yīng)一個(gè)RunLoop對象,每條線程都有唯一一個(gè)與之對應(yīng)的 RunLoop 對象
RunLoop保存在一個(gè)全局的Dictionary里王凑,線程作為key,RunLoop作為value搪柑。
(key:線程 ,value:runloop)RunLoop 對象在第一次獲取 RunLoop 時(shí)創(chuàng)建荤崇,銷毀則是在線程結(jié)束的時(shí)候拌屏。
主線程的 RunLoop 對象系統(tǒng)自動(dòng)幫助我們創(chuàng)建好了潮针,而子線程的 RunLoop對象需要我們主動(dòng)創(chuàng)建和維護(hù)术荤。
這里主線程 runloop自動(dòng)創(chuàng)建,可能會被面試官深問是什么時(shí)候創(chuàng)建的每篷,怎么創(chuàng)建的瓣戚?看怎么回答
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
這是app程序,函數(shù)入口mian,在main里面焦读,直接return 了UIApplicationMain函數(shù)的返回值子库。一般rutern不是直接返回,這個(gè)就結(jié)束了嗎矗晃?為什么這里能一直保持程序不退出呢仑嗅?
其實(shí) UIApplicationMain 函數(shù)內(nèi)部幫我們開啟了主線程的 RunLoop, 內(nèi)部擁有一個(gè)無限循環(huán)的代碼(理解成do...while就OK)张症,這樣仓技,UIApplicationMain函數(shù)就不會立刻返回,只要程序不退出/崩潰俗他,它就一直循環(huán)脖捻。
3.runloop五種模式
- kCFRunLoopDefaultMode:App的默認(rèn)Mode,通常主線程是在這個(gè)Mode下運(yùn)行
- UITrackingRunLoopMode:界面跟蹤 Mode兆衅,用于 ScrollView 追蹤觸摸滑動(dòng)地沮,保證界面滑動(dòng)時(shí)不受其他 Mode 影響
- UIInitializationRunLoopMode: 在剛啟動(dòng) App 時(shí)第進(jìn)入的第一個(gè) Mode嗜浮,啟動(dòng)完成后就不再使用,會切換到kCFRunLoopDefaultMode
- GSEventReceiveRunLoopMode: 接受系統(tǒng)事件的內(nèi)部 Mode摩疑,通常用不到
- kCFRunLoopCommonModes: 這是一個(gè)占位用的Mode危融,作為標(biāo)記kCFRunLoopDefaultMode和UITrackingRunLoopMode用,并不是一種真正的Mode
現(xiàn)實(shí)中,需要用到runloop mode的時(shí)候雷袋,指定kCFRunLoopCommonModes就行了专挪,對于defaultMode與trackingMode來回切太麻煩,頭疼吧
面試被問到這個(gè)知識點(diǎn)片排,肯定會問 NSTimer
1)NSTimer 倒計(jì)時(shí)精確不準(zhǔn)寨腔。自測的話可以看打印log時(shí)間,只要是runloop的影響率寡,雖然我們把它的mode 設(shè)置成commondMode 還是會受影響迫卢;
2)NSTimer 循環(huán)引用的問題。怎么解決冶共,要么寫一個(gè)繼承NSObject類來實(shí)現(xiàn)乾蛤,要么用GCD來寫。具體相關(guān)代碼自己搜咯 [\斜眼笑]捅僵。
補(bǔ)充一下家卖,GCD并不是基于RunLoop的(除了dispatch 調(diào)度到main queue)
上面都是基本知識,接下里庙楚,還會問什么上荡,就是原理東西了,比如:runloop是怎么處理事件的馒闷?
觸摸事件:關(guān)鍵詞source1 souce0 酪捡,響應(yīng)鏈hit-Testing等等
那就看我發(fā)布的這篇文章
iOS 觸摸事件 hitTest touches nextResponder
環(huán)環(huán)相扣,面試就是那么難纳账,腦子里面沒有點(diǎn)墨水不行逛薇,哈哈,知道為什么你的隔壁同事為什么薪資比你高了吧疏虫,寫代碼可以不行永罚,但是能說,說的好卧秘,薪資高呢袱,我也羨慕他們,沒事斯议,跟我混产捞,我?guī)銈冏呱蠋p峰(吹水了哈,共同探討哼御,大家多指點(diǎn)坯临,一起進(jìn)步咯)
重點(diǎn)來了焊唬,Runloop 實(shí)際項(xiàng)目開發(fā)應(yīng)用場景用到了什么?
Runloop 應(yīng)用場景:
1)子線程 常駐存活
用于多次重復(fù)使用到的情景(網(wǎng)絡(luò)請求),如果存活看靠,就不用重復(fù)創(chuàng)建-銷毀
2)讓UITableView赶促、UICollectionView等延遲加載圖片
[imageView performSelector:@selector(setImage:) withObject:image afterDelay:0nModes:@[NSDefaultRunLoopMode]];
這算一個(gè)優(yōu)化點(diǎn),這里用的defaultMode挟炬,就是滾動(dòng)的時(shí)候不加載圖片鸥滨,停止?jié)L動(dòng)加載圖片
3)定時(shí)器-Timer (GCD來做定時(shí)器會更好)
4)阻止app崩潰
5)檢測app卡頓情況
有問題,請賜教谤祖,抱拳
ps:拓展
- iOS應(yīng)用崩潰婿滓,常見的崩潰信息有EXC_BAD_ACCESS、SIGABRT XXXXXXX,而這里分為兩種情況粥喜,一種是未被捕獲的異常凸主,我們只需要添加一個(gè)回調(diào)函數(shù),并在應(yīng)用啟動(dòng)時(shí)調(diào)用一個(gè) API即可额湘;另一種是直接發(fā)送的 SIGABRT XXXXXXX,這里我們也需要監(jiān)聽各種信號卿吐,然后添加回調(diào)函數(shù)。
針對情況一锋华,其實(shí)我們都見過嗡官。我們在收集App崩潰信息時(shí),需要添加一個(gè)函數(shù) NSSetUncaughtExceptionHandler(&HandleException)毯焕,參數(shù) 是一個(gè)回調(diào)函數(shù)衍腥,在回調(diào)函數(shù)里獲取到異常的原因,當(dāng)前的堆棧信息等保存到 dump文件芥丧,然后供下次打開App時(shí)上傳到服務(wù)器紧阔。
其實(shí)坊罢,我們在HandleException回調(diào)函數(shù)中续担,可以獲取到當(dāng)前的RunLoop,然后獲取該RunLoop中的所有Mode活孩,手動(dòng)運(yùn)行一遍
針對情況二物遇,首先針對多種要捕獲的信號,設(shè)置好回調(diào)函數(shù)憾儒,然后也是在回調(diào)函數(shù)中獲取RunLoop询兴,然后拿到所有的Mode,手動(dòng)運(yùn)行一遍起趾。 - app卡頓情況監(jiān)測诗舰, 我們只需要在主線程的RunLoop中添加一個(gè)observer,檢測從 kCFRunLoopBeforeSources 到 kCFRunLoopBeforeWaiting 花費(fèi)的時(shí)間 是否過長训裆。如果花費(fèi)的時(shí)間大于某一個(gè)闕值眶根,我們就認(rèn)為有卡頓蜀铲,并把當(dāng)前的線程堆棧轉(zhuǎn)儲到文件中,并在以后某個(gè)合適的時(shí)間属百,將卡頓信息文件上傳到服務(wù)器记劝。