談?wù)凮bjective-C的Runtime機(jī)制

1. 什么是Runtime機(jī)制

? ? ?Runtime[1]是一套比較底層的C語(yǔ)言庫(kù), 由一系列函數(shù)和數(shù)據(jù)結(jié)構(gòu)組成碾阁,包含了很多底層的C語(yǔ)言API。它主要是完成了Objective-C (OC)運(yùn)行時(shí)的兩件事:類(lèi)的封裝和消息傳遞。

? ? ?本文從1. 類(lèi)的封裝管理 2.消息傳遞?總結(jié)在我學(xué)習(xí)中對(duì)Runtime的認(rèn)識(shí)坪创,隨后總結(jié)我所能理解到的Runtime機(jī)制的優(yōu)劣只祠。

2. 類(lèi)的封裝

? ? ?由于Runtime主要是用C語(yǔ)言來(lái)實(shí)現(xiàn),因此可以看到Runtime.h中蝎毡,大量使用了struct來(lái)描述對(duì)象和類(lèi)厚柳,而也使用了許多方法來(lái)封裝函數(shù)和結(jié)構(gòu)體,這使得OC在運(yùn)行中具有了面向?qū)ο蟮奶匦浴?/p>

? ? ?首先沐兵,OC的實(shí)例(Instance)别垮,類(lèi)(Class)以及相對(duì)應(yīng)元類(lèi)(metaClass)的繼承體系如圖


????類(lèi)的實(shí)例通過(guò)isa指針指向這個(gè)實(shí)例的Class,這個(gè)對(duì)象的Class中的isa指針則指向這個(gè)類(lèi)的metaClass扎谎。當(dāng)我們向一個(gè)實(shí)例發(fā)送消息碳想,runtime會(huì)在這個(gè)實(shí)例isa指向的Class方法列表中查找方法烧董;而向一個(gè)類(lèi)發(fā)送消息時(shí),會(huì)在該類(lèi)的metaClass的方法列表中進(jìn)行查找胧奔。

? ? ?在這個(gè)繼承體系的實(shí)現(xiàn)中逊移,Runtime通過(guò)大量以class_,objc_為前綴的方法(例如下面的兩個(gè)方法)來(lái)封裝結(jié)構(gòu)體和函數(shù)。使得使用者可以直接操作Class龙填,添加方法胳泉,協(xié)議等,也可以操作成員變量和屬性岩遗。

? ??在此基礎(chǔ)上扇商,Runtime所實(shí)現(xiàn)最強(qiáng)大的功能是支持在程序運(yùn)行的時(shí)候創(chuàng)建和修改類(lèi),實(shí)例操作函數(shù)[2]和運(yùn)行時(shí)創(chuàng)建關(guān)聯(lián)對(duì)象(Associated Object)宿礁。 例如:當(dāng)我們想為Category增加成員變量時(shí)案铺,可以利用以下兩個(gè)函數(shù),在運(yùn)行時(shí)動(dòng)態(tài)地增加成員變量:

? ??void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)

? ? id objc_getAssociatedObject(id object, const void *key)

3. 消息傳遞

? ? ?OC除了具有靜態(tài)語(yǔ)言的特征之外梆靖,也具有動(dòng)態(tài)語(yǔ)言的特性:動(dòng)態(tài)類(lèi)型(Dynamic typing),動(dòng)態(tài)綁定(Dynamic binding)和動(dòng)態(tài)加載(Dynamic loading)控汉。Runtime機(jī)制使得這三個(gè)特性得以實(shí)現(xiàn),讓程序可以在運(yùn)行時(shí)才判斷其該有的行為涤姊,而不是像靜態(tài)語(yǔ)言一樣在編譯時(shí)就確定了行為暇番。

? ? ?一個(gè)OC程序在運(yùn)行時(shí),除了在上文第2部分提到的思喊,還主要在以下兩個(gè)場(chǎng)景中運(yùn)用了Runtime機(jī)制:結(jié)合動(dòng)態(tài)綁定的類(lèi)實(shí)現(xiàn)壁酬,結(jié)合動(dòng)態(tài)類(lèi)型的NSObject方法。三個(gè)方面互有關(guān)聯(lián)恨课,也有不同舆乔,流程如下:

3.1 結(jié)合動(dòng)態(tài)綁定的類(lèi)實(shí)現(xiàn)

首先,一個(gè)OC程序在調(diào)用一個(gè)方法時(shí)的流程如下:? ?

發(fā)送消息 -> 動(dòng)態(tài)方法決議機(jī)制 -> 消息轉(zhuǎn)發(fā)

步驟一:?發(fā)送消息(Message):若成功則調(diào)用方法剂公,若失敗且實(shí)現(xiàn)了動(dòng)態(tài)方法決議機(jī)制則進(jìn)入步驟二

步驟二:動(dòng)態(tài)方法決議機(jī)制(Dynamic Method Resolution): 若成功則調(diào)用方法希俩,若失敗且實(shí)現(xiàn)了消息轉(zhuǎn)發(fā)機(jī)制則進(jìn)入Step 3

步驟三:消息轉(zhuǎn)發(fā)(Messaging Forwarding): 如提供了消息轉(zhuǎn)發(fā),則不會(huì)有錯(cuò)誤提示纲辽。不提供則報(bào)錯(cuò)

三個(gè)步驟執(zhí)行是有先后順序且不相關(guān)颜武。下面通過(guò)順序分析三個(gè)步驟來(lái)解釋Runtime在其中的應(yīng)用:

3.1.1 三個(gè)步驟完成調(diào)用方法

Step 1: 消息傳遞(Message)

? ? ?OC程序運(yùn)行時(shí)依賴(lài)消息傳遞來(lái)實(shí)現(xiàn)動(dòng)態(tài)綁定的方法調(diào)用:當(dāng)我們需要調(diào)用一個(gè)實(shí)例的方法,程序所做的是在運(yùn)行時(shí)才確定向某個(gè)類(lèi)的receiver發(fā)送消息拖吼,receiver在收到消息后鳞上,從自身的實(shí)現(xiàn)中通過(guò)@selector/SEL去尋找響應(yīng)這條消息對(duì)應(yīng)的IMP。

? ??//默認(rèn)帶有兩個(gè)隱形的參數(shù) 吊档,若有參數(shù)則為objc_msgSend(receiver, selector, arg1, ...)

?????[receiver messasge] -> objc_msgSend(receiver, selector)

? ? ?在上面的轉(zhuǎn)化中篙议,objc_msgSend會(huì)通過(guò)receiver的isa指針找到receiver對(duì)應(yīng)的類(lèi),再在該類(lèi)或該類(lèi)的父類(lèi)(沿著繼承體系繼續(xù)向上查找,如圖3.1)中的struct objc_cache *cache 和 struct objc_method_list ** methodLists尋找到所需方法鬼贱。[3]

????在objc_msgSend函數(shù)中移怯。首先通過(guò)obj的isa指針找到obj對(duì)應(yīng)的class。在Class中先去cache中 通過(guò)SEL查找對(duì)應(yīng)函數(shù)method(猜測(cè)cache中method列表是以SEL為key通過(guò)hash表來(lái)存儲(chǔ)的这难,這樣能提高函數(shù)查找速度)

????若 cache中未找到舟误。再去methodList中查找,若methodlist中未找到雁佳,則取superClass中查找脐帝。若能找到同云,則將method加 入到cache中糖权,以方便下次查找,并通過(guò)method中的函數(shù)指針跳轉(zhuǎn)到對(duì)應(yīng)的函數(shù)中去執(zhí)行炸站。


Step 2: 動(dòng)態(tài)方法決議機(jī)制(Dynamic Method Resolution)

? ? ?這個(gè)機(jī)制是在當(dāng)在子類(lèi)到父類(lèi)都尋找不到對(duì)應(yīng)的方法時(shí)星澳,讓我們能夠在運(yùn)行時(shí)動(dòng)態(tài)地為一個(gè)selector提供實(shí)現(xiàn)。依賴(lài)NSObject.h中的如下方法為類(lèi)或?qū)嵗黾有碌念?lèi)方法或?qū)嵗椒ā?/p>

? ? + (BOOL)resolveClassMethod:(SEL)sel __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);

? ? + (BOOL)resolveInstanceMethod:(SEL)sel __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);

Step 3: 消息轉(zhuǎn)發(fā)(Messaging Forwarding)

? ? ?Runtime會(huì)把消息有關(guān)的全部細(xì)節(jié)都封裝到NSInvocation對(duì)象中旱易,再給receiver最后一次機(jī)會(huì)禁偎,令其設(shè)法解決當(dāng)前還未處理的這條消息。

? ? ?Runtime會(huì)通過(guò)回調(diào)一個(gè)類(lèi)方法來(lái)尋求動(dòng)態(tài)添加方法的支持阀坏。如果receiver仍然無(wú)法正常響應(yīng)如暖,則Runtime會(huì)繼續(xù)向receiver詢(xún)問(wèn)是否有其它對(duì)象可以處理這條消息,若返回能夠處理的對(duì)象忌堂,Runtime會(huì)把消息轉(zhuǎn)給返回的對(duì)象盒至,消息轉(zhuǎn)發(fā)流程也就結(jié)束。


3.1.2 利用Method Implementations(IMP)直接調(diào)用方法實(shí)現(xiàn)

? ? ?之前在3.1.1中提到了消息傳遞過(guò)程中通過(guò)@selector/SEL去查找消息對(duì)應(yīng)的IMP士修。而我們也可以通過(guò)IMP省去Runtime消息傳遞過(guò)程中的查找操作枷遂。

? ? ?先看看Runtime.h中可以看到關(guān)于一個(gè)方法的結(jié)構(gòu)定義如下,這個(gè)結(jié)構(gòu)實(shí)際上完成了SEL和對(duì)應(yīng)IMP的一個(gè)映射

? ?? struct objc_method {

? ? ? ? SEL method_name? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;

? ? ? ? char *method_types? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;

? ? ? ? IMP method_imp? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;

? ? }

????method_types:char指針棋嘲,存儲(chǔ)著方法的參數(shù)類(lèi)型和返回值類(lèi)型酒唉。

? ? ?SEL:一個(gè)Int類(lèi)型的一個(gè)地址,地址中存放著方法的名字沸移,它僅僅和方法名相關(guān)痪伦。因此不同類(lèi)中可能存在擁有相同SEL的方法。工程中所有的SEL形成了一個(gè)Set雹锣。我的理解這個(gè)Set的存在是為了提高方法的查詢(xún)速度网沾。

? ? ?IMP:它本質(zhì)可以說(shuō)是一個(gè)函數(shù)指針,即方法代碼的入口點(diǎn)笆制,定義如下:

? ?? id (*IMP)(id, SEL, ...)

????因此IMP也可以在當(dāng)一個(gè)消息要被發(fā)送給某個(gè)對(duì)象很多次的時(shí)候直接調(diào)用(例如下例在for循環(huán)中調(diào)用多次setFill:绅这,直接使用NSObject類(lèi)中的methodForSelector:可以獲得一個(gè)指向方法實(shí)現(xiàn)的指針來(lái)進(jìn)行優(yōu)化)。 這樣省去了Runtime消息傳遞時(shí)的查找操作在辆,會(huì)比直接向?qū)ο蟀l(fā)送消息高效一些证薇。

3.2 結(jié)合動(dòng)態(tài)類(lèi)型的NSObject方法

? ? ?由于大部分的對(duì)象都是由NSObject繼承而來(lái)度苔。因此也繼承了了NSObject的屬性和內(nèi)存分配方法(如下),這些方法均用于在運(yùn)行時(shí)取得信息的方法浑度。

?- (BOOL)isKindOfClass:(Class)aClass;

? - (BOOL)isMemberOfClass:(Class)aClass;

?- (BOOL)conformsToProtocol:(Protocol *)aProtocol;

動(dòng)態(tài)特性的靈活使得id類(lèi)型在Protocol-Delegate的實(shí)現(xiàn)中也大量被使用寇窑,主要是把delegate指針類(lèi)型定義為id,從而使得運(yùn)行時(shí)實(shí)現(xiàn)動(dòng)態(tài)替換箩张。

4. Runtime的應(yīng)用

借用 http://www.reibang.com/p/ab966e8a82e2 的圖

4.1 動(dòng)態(tài)添加一個(gè)類(lèi)(KVO的能夠?qū)崿F(xiàn)也依賴(lài)于這個(gè)特性)

4.2 字典轉(zhuǎn)模型 & 自動(dòng)歸檔解檔?

4.3 動(dòng)態(tài)交換類(lèi)方法/對(duì)象方法/系統(tǒng)方法

5. Runtime機(jī)制的優(yōu)劣

優(yōu)點(diǎn):更加靈活甩骏。

? ? ?Runtime環(huán)境注冊(cè)所有全局的類(lèi),函數(shù)先慷,變量等等信息等等饮笛,我們可以無(wú)限的為這個(gè)層增加必要的功能,寫(xiě)代碼時(shí)更具靈活性论熙。調(diào)用函數(shù)時(shí)候福青,會(huì)先從這個(gè)運(yùn)行時(shí)環(huán)境里檢測(cè)所以所有的可能,而并不是JMP到一個(gè)非法地址就一定會(huì)Crash脓诡。這就極大增加了程序的靈活性无午。

缺點(diǎn):增加損耗。

? ? ?很顯然Runtime的機(jī)制帶來(lái)了CPU計(jì)算的損耗祝谚。而也可以看出Runtime機(jī)制為了提高效率也設(shè)計(jì)了SEL, objc_cache *cache來(lái)緩存使用過(guò)的selector及對(duì)應(yīng)的方法的地址等方法宪迟,從而提高效率。

5. Reference

[1] AppleRuntime Guide

[2] Objective-C Runtime 運(yùn)行時(shí)之一:類(lèi)與對(duì)象

[3] Objective-C總Runtime的那點(diǎn)事兒

[4] 深入學(xué)習(xí)Objective-C語(yǔ)言的動(dòng)態(tài)特性:

[5] Understanding the Objective-C Runtime

[6] 刨根問(wèn)題Objective-C Runtime

[7] Objective-C 與 Runtime:為什么是這樣交惯?

http://www.reibang.com/p/ab966e8a82e2

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末次泽,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子商玫,更是在濱河造成了極大的恐慌箕憾,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拳昌,死亡現(xiàn)場(chǎng)離奇詭異袭异,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)炬藤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)御铃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人沈矿,你說(shuō)我怎么就攤上這事上真。” “怎么了羹膳?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵睡互,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng)就珠,這世上最難降的妖魔是什么寇壳? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮妻怎,結(jié)果婚禮上壳炎,老公的妹妹穿的比我還像新娘。我一直安慰自己逼侦,他們只是感情好匿辩,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著榛丢,像睡著了一般铲球。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上涕滋,一...
    開(kāi)封第一講書(shū)人閱讀 49,772評(píng)論 1 290
  • 那天睬辐,我揣著相機(jī)與錄音挠阁,去河邊找鬼宾肺。 笑死,一個(gè)胖子當(dāng)著我的面吹牛侵俗,可吹牛的內(nèi)容都是我干的锨用。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼隘谣,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼增拥!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起寻歧,我...
    開(kāi)封第一講書(shū)人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤掌栅,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后码泛,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體猾封,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年噪珊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了晌缘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡痢站,死狀恐怖磷箕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情阵难,我是刑警寧澤岳枷,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響空繁,放射性物質(zhì)發(fā)生泄漏氢烘。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一家厌、第九天 我趴在偏房一處隱蔽的房頂上張望播玖。 院中可真熱鬧,春花似錦饭于、人聲如沸蜀踏。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)果覆。三九已至,卻和暖如春殖熟,著一層夾襖步出監(jiān)牢的瞬間局待,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工菱属, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留钳榨,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓纽门,卻偏偏與公主長(zhǎng)得像薛耻,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子赏陵,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉饼齿,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,686評(píng)論 0 9
  • 我們常常會(huì)聽(tīng)說(shuō) Objective-C 是一門(mén)動(dòng)態(tài)語(yǔ)言,那么這個(gè)「動(dòng)態(tài)」表現(xiàn)在哪呢蝙搔?我想最主要的表現(xiàn)就是 Obje...
    Ethan_Struggle閱讀 2,182評(píng)論 0 7
  • 轉(zhuǎn)載:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麥子閱讀 729評(píng)論 0 2
  • 本文詳細(xì)整理了 Cocoa 的 Runtime 系統(tǒng)的知識(shí)缕溉,它使得 Objective-C 如虎添翼,具備了靈活的...
    lylaut閱讀 795評(píng)論 0 4
  • 文中的實(shí)驗(yàn)代碼我放在了這個(gè)項(xiàng)目中吃型。 以下內(nèi)容是我通過(guò)整理[這篇博客] (http://yulingtianxia....
    茗涙閱讀 914評(píng)論 0 6