Runtime與Runloop 精簡理解

最近面試總被問道這兩方面的問題,以前了解的還是比較片面,把自己理解的做一下總結(jié);

我所記錄的不是大神那種把源碼一條條給你分析,我說的會通俗易懂針對于面試;

1.什么是Runtime?

一般會想到 【運(yùn)行時】【獲取屬性】【獲取方法列表】用途【字典轉(zhuǎn)模型】;

OC的函數(shù)本質(zhì)是消息的發(fā)送,屬于動態(tài)調(diào)用過程。我們寫 OC 代碼,它在運(yùn)行的時候也是轉(zhuǎn)換成了 runtime 方式運(yùn)行的,會根據(jù)函數(shù)的名稱找到對應(yīng)的函數(shù)來調(diào)用。

例子:看下運(yùn)行時都做了什么

objc = [objc init];//這個方法的調(diào)用 下邊在runtime 底層實際寫法

objc = objc_msgSend(objc, @selector(init));//object是一個objc_object結(jié)構(gòu)體

_msgSend下面在解釋

1.1什么是:objc_object ?答:動態(tài)類型id其實就是一個objc_object。

// 在objc.h中找到相關(guān)代碼

typedefstructobjc_class*Class;

structobjc_object{

Class isa? OBJC_ISA_AVAILABILITY;

};

可以看出一個類的實例,即一個對象,其實在runtime時刻是一個objc_class結(jié)構(gòu)體,而結(jié)構(gòu)體里面只有一個指向objc_class的指針isa。

1.2什么是isa

isa指針. 對象的指針—>類,類的指針—>元類,元類—>根元類—>自己 形成閉環(huán);

1.3什么是objc_class

看到這里是不是知道運(yùn)行時為什么可獲取屬性 和方法了 因為這些都在class里

objc_method_list方法鏈表 先看下objc_method的結(jié)構(gòu)

SEL

從SEL類型的成員為method_name可以知道,SEL大概代表一個方法的名字,作用是標(biāo)記方法區(qū)分方法

IMP

IMP是一個函數(shù)指針,指向objc_method對應(yīng)方法的實現(xiàn)部分。

1.4消息傳遞與轉(zhuǎn)發(fā)機(jī)制objc_msgSend()

上面介紹了方法在哪里 ?接下來是我們在調(diào)用方法(發(fā)消息)runtime 都做了什么

消息傳遞

1.objc_msgSend()函數(shù)會根據(jù)調(diào)用的對象isa指針 找到所屬的class里面的objc_method_list方法列表

然后從上向下遍歷,找到SEL相符的方法名,根據(jù)IMP指針跳轉(zhuǎn)到方法的實現(xiàn)代碼,調(diào)用這個方法的實現(xiàn)

2.如果找不到接收者,會根據(jù)所屬類的superClass指針,沿著類的繼承體系繼續(xù)向上查找(向父類查找),如果 能找到與名稱相符的方法,就根據(jù)IMP指針跳轉(zhuǎn)到方法的實現(xiàn)代碼,調(diào)用這個方法的實現(xiàn)。

3.如果在繼承體系中還是找不到相符的方法,此時就會執(zhí)行”消息轉(zhuǎn)發(fā)(message forwarding)“操作。

消息轉(zhuǎn)發(fā)

1.如果在整個類的繼承體系中還是找不到與相符的方法,也就是對象或者類對象收到了無法解讀的消息,那么就會進(jìn)入到消息轉(zhuǎn)發(fā)環(huán)節(jié)。

2.消息轉(zhuǎn)發(fā)分為兩個階段。第一階段叫做“動態(tài)方法解析(dynamic method resolution)”,或者叫“動態(tài)方法決議”。第二階段涉及到“完整的消息轉(zhuǎn)發(fā)機(jī)制(full forwarding mechanism)”,或者叫“完整的消息轉(zhuǎn)發(fā)原理”。

動態(tài)方法解析的意思就是,征詢消息接受者所屬的類,看其是否能動態(tài)添加方法,以處理當(dāng)前“這個未知的選擇子(unknown selector)“。實例對象在接受到無法解讀的消息后,首先會調(diào)用其所屬類的下列類方法:

1+?(BOOL)resolveInstanceMethod:(SEL)selector

類對象在接受到無法解讀的消息后,那么運(yùn)行期系統(tǒng)就會調(diào)用另外的一個方法,如下:

1+?(BOOL)resolve【瑞走】ClassMethod:(SEL)selector

如果運(yùn)行期系統(tǒng)已經(jīng)執(zhí)行完了動態(tài)方法解析,那么消息接受者自己就無法再以動態(tài)新增方法的形式來響應(yīng)包含該未知選擇子的消息了,此時就進(jìn)入了第二階段——完整的消息轉(zhuǎn)發(fā)。運(yùn)行期系統(tǒng)會請求消息接受者以其他手段來處理與消息相關(guān)的方法調(diào)用。

(2.2)完整的消息轉(zhuǎn)發(fā)

完整的消息轉(zhuǎn)發(fā)又分為兩個階段,第一階段稱為備援接受者(replacement receiver),第二階段才是啟動完整的消息轉(zhuǎn)發(fā)機(jī)制。

(2.2.1)備援接收者(replacement receiver)

當(dāng)前接受者如果不能處理這條消息,運(yùn)行期系統(tǒng)會請求當(dāng)前接受者讓其他接受者處理這條消息,與之對應(yīng)的方法是:

1-?(id)forwardingTargetForSelector:(SEL)selector

方法參數(shù)代表未知的選擇子,返回值為備援接受者,若當(dāng)前接受者能找到備援接受者,就直接返回,這個未知的選擇子將會交由備援接受者處理。如果找不到備援接受者,就返回nil,此時就會啟用”完整的消息轉(zhuǎn)發(fā)機(jī)制“。

(2.2.2)完整的消息轉(zhuǎn)發(fā)

如果轉(zhuǎn)發(fā)算法已經(jīng)來到了這一步,那么代表之前的所有轉(zhuǎn)發(fā)嘗試都失敗了,此時只能啟用完整的消息轉(zhuǎn)發(fā)機(jī)制。完整的消息轉(zhuǎn)發(fā)機(jī)制是這樣的:首先創(chuàng)建NSInvocation對象,把尚未處理的那條消息有關(guān)的全部細(xì)節(jié)封裝于這個NSInvocation對象中。此對象中包含選擇子(selector)、目標(biāo)(target)及參數(shù)。在觸發(fā)NSInvocation對象時,”消息派發(fā)系統(tǒng)(message-dispatch system)“將親自觸發(fā),把消息派發(fā)給目標(biāo)對象。此步驟中會調(diào)用下面這個方法來轉(zhuǎn)發(fā)消息:

1-?(void)forwardInvocation:(NSInvocation?*)invocation

消息派發(fā)系統(tǒng)觸發(fā)消息前,會以某種方式改變消息內(nèi)容,包括 但不限于額外追加一個參數(shù)、改變選擇子等。

實現(xiàn)此方法時,如果發(fā)現(xiàn)調(diào)用操作不應(yīng)該由本類處理,則需要沿著繼承體系,調(diào)用父類的同名方法,這樣一來,繼承體系中的每個類都有機(jī)會處理這個調(diào)用請求,直至rootClass,也就是NSObject類。如果最后調(diào)用了NSObject的類方法,那么該方法還會繼而調(diào)用”doesNotRecognizeSelector:“以拋出異常,此異常表明選擇子最終也未能得到處理。消息轉(zhuǎn)發(fā)到此結(jié)束。

小結(jié)

* 若對象無法響應(yīng)某個選擇子,則進(jìn)入消息轉(zhuǎn)發(fā)流程。

* 通過運(yùn)行期的動態(tài)方法解析功能,我們可以在需要用到某個方法時再將其加入類中。

* 對象可以把其無法解讀的某些選擇子轉(zhuǎn)交給其他對象處理。

* 經(jīng)過上述兩步之后,如果還是沒辦法處理選擇子,那就啟動完整的消息轉(zhuǎn)發(fā)機(jī)制。

1.什么是RunLoop?

通常所說的RunLoop指的是NSRunloop或者CFRunloopRef,CFRunloopRef是純C的函數(shù),而NSRunloop僅僅是CFRunloopRef的OC封裝,內(nèi)部其實是一個_do while_循環(huán),這也正是Runloop運(yùn)行的本質(zhì),只是不同于我們自己寫的循環(huán)它在休眠時幾乎不會占用系統(tǒng)資源,當(dāng)然這是由于系統(tǒng)內(nèi)核負(fù)責(zé)實現(xiàn)的,也是Runloop精華所在!

Runloop和線程的關(guān)系

主線程默認(rèn)開啟runloop ?子線程要去獲取 ?兩個自動獲取的函數(shù):CFRunLoopGetMain() 和 CFRunLoopGetCurrent()

蘋果用 RunLoop 實現(xiàn)的功能

AutoreleasePool 自動釋放池


RunLoop啟動的時候創(chuàng)建autoreleasePool

RunLoop結(jié)束的時候銷毀autoreleasePool

當(dāng)RunLoop進(jìn)行休眠的時候,將會將之前的autoreasePool銷毀,同時創(chuàng)建新的autoreleasePool

App啟動后,蘋果在主線程 RunLoop 里注冊了兩個 Observer,其回調(diào)都是 _wrapRunLoopWithAutoreleasePoolHandler()。

第一個 Observer 監(jiān)視的事件是 Entry(即將進(jìn)入Loop),其回調(diào)內(nèi)會調(diào)用 _objc_autoreleasePoolPush() 創(chuàng)建自動釋放池。其 order 是-2147483647,優(yōu)先級最高,保證創(chuàng)建釋放池發(fā)生在其他所有回調(diào)之前。

第二個 Observer 監(jiān)視了兩個事件: BeforeWaiting(準(zhǔn)備進(jìn)入休眠) 時調(diào)用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 釋放舊的池并創(chuàng)建新池;Exit(即將退出Loop) 時調(diào)用 _objc_autoreleasePoolPop() 來釋放自動釋放池。這個 Observer 的 order 是 2147483647,優(yōu)先級最低,保證其釋放池子發(fā)生在其他所有回調(diào)之后。

在主線程執(zhí)行的代碼,通常是寫在諸如事件回調(diào)、Timer回調(diào)內(nèi)的。這些回調(diào)會被 RunLoop 創(chuàng)建好的 AutoreleasePool 環(huán)繞著,所以不會出現(xiàn)內(nèi)存泄漏,開發(fā)者也不必顯示創(chuàng)建 Pool 了。

事件響應(yīng)

蘋果注冊了一個 Source1 (基于 mach port 的) 用來接收系統(tǒng)事件,其回調(diào)函數(shù)為 __IOHIDEventSystemClientQueueCallback()。

當(dāng)一個硬件事件(觸摸/鎖屏/搖晃等)發(fā)生后,首先由 IOKit.framework 生成一個 IOHIDEvent 事件并由 SpringBoard 接收。_UIApplicationHandleEventQueue() 會把 IOHIDEvent 處理并包裝成 UIEvent 進(jìn)行處理或分發(fā),其中包括識別 UIGesture/處理屏幕旋轉(zhuǎn)/發(fā)送給 UIWindow 等。通常事件比如 UIButton 點(diǎn)擊、touchesBegin/Move/End/Cancel 事件都是在這個回調(diào)中完成的。

手勢識別 ? ??界面更新 ? ?

定時器 ?

為什么把NSTimer對象以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主運(yùn)行循環(huán)以后,滑動scrollview的時候NSTimer卻不動了?

nstime對象是在NSDefaultRunLoopMode下面調(diào)用消息的,但是當(dāng)我們滑動scrollview的時候,NSDefaultRunLoopMode模式就自動切換到UITrackingRunLoopMode模式下面,卻不可以繼續(xù)響應(yīng)nstime發(fā)送的消息。所以如果想在滑動scrollview的情況下面還調(diào)用nstime的消息,我們可以把nsrunloop的模式更改為NSRunLoopCommonModes

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 最近面試總被問道這兩方面的問題,以前了解的還是比較片面,把自己理解的做一下總結(jié); 我所記錄的不是大神那種把源碼一條...
    雷3雷閱讀 821評論 0 1
  • runtime 和 runloop 作為一個程序員進(jìn)階是必須的,也是非常重要的, 在面試過程中是經(jīng)常會被問到的, ...
    made_China閱讀 1,269評論 0 7
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,041評論 0 9
  • runtime 和 runloop 作為一個程序員進(jìn)階是必須的,也是非常重要的, 在面試過程中是經(jīng)常會被問到的, ...
    SOI閱讀 22,020評論 3 63
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,634評論 1 32

友情鏈接更多精彩內(nèi)容