1. 河豚號 > 生活百科 >

performselector實(shí)現(xiàn)原理(詳解performselector應(yīng)用場景)

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,請看下圖

 

中高級 iOS 必備知識點(diǎ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ù)里面的偽代碼如下:

 

中高級 iOS 必備知識點(diǎn)之 RunLoop

 

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 包裝出來的,請看下面:

 

中高級 iOS 必備知識點(diǎn)之 RunLoop

 

獲取當(dāng)前的 RunLoop

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

 

中高級 iOS 必備知識點(diǎn)之 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。

 

中高級 iOS 必備知識點(diǎn)之 RunLoop

 

源碼窺探看一下:CFRunLoopGetCurrent

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

 

中高級 iOS 必備知識點(diǎn)之 RunLoop
中高級 iOS 必備知識點(diǎn)之 RunLoop

 

從字典也能看出來是一對一的關(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 的底層源碼:

 

中高級 iOS 必備知識點(diǎn)之 RunLoop

 

就是上面這個(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ǔ)的是什么?

 

中高級 iOS 必備知識點(diǎn)之 RunLoop

 

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

 

中高級 iOS 必備知識點(diǎn)之 RunLoop

 

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è)我們看下下面的代碼:

 

中高級 iOS 必備知識點(diǎn)之 RunLoop

 

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

聯(lián)系我們

在線咨詢:點(diǎn)擊這里給我發(fā)消息

微信號:15705946153

工作日:9:30-18:30,節(jié)假日休息