趣談iOS運行時之方法調(diào)用原理

導(dǎo)語

一個成熟的計算機語言必然有豐富的體系,復(fù)雜的容錯機制,處理邏輯以及判斷邏輯梳虽。但這些復(fù)雜的邏輯都是圍繞一個主線豐富和展開的,所以在學習計算機語言的時候灾茁,先掌握核心窜觉,然后了解其原理,明白程序語言設(shè)計的實質(zhì)和當時選擇這種處理方式的原因是極其必要的北专,而且也是學習語言的捷徑禀挫。

所以在學習的過程中,需要把握幾個核心
先專注主線拓颓,后豐富周邊语婴;
先宏觀了解,后微觀精通;
多設(shè)身處地思考驶睦,理解代碼設(shè)計的原因;
理解代碼設(shè)計的原理和優(yōu)化


OC中處理方法的業(yè)務(wù)邏輯和其他語言不同砰左,OC語言是動態(tài)語言(動態(tài)綁定動態(tài)加載(dynamatic binding),動態(tài)類型)场航。其中動態(tài)加載就涉及到OC的運行時菜职。在OC中,方法是動態(tài)實現(xiàn)的旗闽,調(diào)用方法實際就是在發(fā)送消息酬核。
試想一下,一個方法的實現(xiàn)必然包含三個部分:

1.執(zhí)行方法的對象
2.方法名稱
3.不確定的參數(shù)

SEL只是一個方法名稱适室,IMP才是執(zhí)行方法最終的函數(shù)嫡意。IMP 是一個函數(shù)指針,包含一個接收消息的對象 id(self 指針), 調(diào)用方法的選標 SEL (方法名),以及不定個數(shù)的參數(shù),并返回一個 id。也就是說 IMP 是消息最終調(diào)用的執(zhí)行代碼,是方法真正的實現(xiàn)代碼 捣辆。


提問時間到了:

  • 動態(tài)和靜態(tài)有什么區(qū)別蔬螟?
  • 執(zhí)行方法是怎么實現(xiàn)的?
  • OC的方法和C語言的函數(shù)原理一樣么汽畴?

動態(tài)和靜態(tài)有區(qū)別的旧巾;首先我們從最表層理解耸序,一個方法的實現(xiàn)必然要包含執(zhí)行者,方法名和不確定的參數(shù)和返回值鲁猩。無論是靜態(tài)或者動態(tài)方法都必須這三個必要元素(動態(tài)和靜態(tài)的區(qū)別就在于在何時確定這些必要元素)坎怪。
方法的執(zhí)行包含編譯和運行兩個過程。

  • 靜態(tài)方法是在編譯時已經(jīng)確定了三個要素廓握,且不能更改搅窿。若類型不對,就會直接發(fā)出警告隙券。
  • 而OC的動態(tài)方法可以直接跳過編譯男应,在運行時才開始添加函數(shù)調(diào)用,決定執(zhí)行方法的三個要素娱仔。這就是動態(tài)方法(至于怎么執(zhí)行沐飘,下面開始講解)

這三個元素是如何確定的呢?首先我們看一段示例代碼

Dog *aDog = [[Dog alloc]init];
[aDog run];

在執(zhí)行方法時牲迫,是怎么確定的呢耐朴?
此時我們需要注意OC內(nèi)部方法的實質(zhì):OC中,方法實現(xiàn)實質(zhì)就是發(fā)送消息恩溅。
[aDog run];代碼的實質(zhì)就是[ objc_sendMsg],它會找到執(zhí)行方法的三個要素谓娃,找到就按照規(guī)則執(zhí)行脚乡。
發(fā)送消息是通過 objc_send(id, SEL, ...) 來實現(xiàn)的,它首先會在對象的類對象的 cachemethodlist 以及父類對象的 cache,methodlist 中依次查找 SEL 對應(yīng) 的 IMP;
如果沒有找到且實現(xiàn)了動態(tài)方法決議機制就會進行決議滨达。

如果沒有實現(xiàn)動態(tài)方法決議機制或決議失敗且實現(xiàn)了消息轉(zhuǎn)發(fā)機制就會進入消息轉(zhuǎn)發(fā)流程,否則程序 crash奶稠。
也就是說如果同時提供了動態(tài)方法決議消息轉(zhuǎn)發(fā),那么動態(tài)方法決議先于消息轉(zhuǎn)發(fā),只有當動態(tài)方法決議依然無法正確決議 selector 的 實現(xiàn),才會嘗試進行消息轉(zhuǎn)發(fā)。當然捡遍,實際過程不可能那么簡單锌订,在開發(fā)語言之初,肯定會完善各種復(fù)雜場景和做了很多優(yōu)化画株,接下來我們一起研究下OC對方法執(zhí)行和擴展和優(yōu)化:

  • 第一步:先找方法
  • 第二步:動態(tài)方法決議
  • 第三部:消息轉(zhuǎn)發(fā)
  • 最后: 報錯

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

通常,給一個對象發(fā)送它不能處理的消息會得到出錯提示,然而,Objective-C運行時系統(tǒng)在拋出錯誤之前, 會給消息接收對象發(fā)送一條特別的消息 forwardInvocation 來通該對象,該消息的唯一參數(shù)是個 NSInvocation 類型的對象——該對象封裝了原始的消息和消息的參數(shù)辆飘。我們可以實現(xiàn) forwardInvocation:方法來對不能處理的消息做一些默認的處理,也可以將消息轉(zhuǎn)發(fā)給其他對 象來處理,而不拋出錯誤。

  • 1,首先去該類的方法cache中查找,如果找到了就返回它;
  • 2,如果沒有找到,就去該類的方法列表中查找谓传。如果在該類的方法列表中找到了,則將 IMP 返回,并將 它加入 cache 中緩存起來蜈项。根據(jù)最近使用原則,這個方法再次調(diào)用的可能性很大,緩存起來可以節(jié)省下次 調(diào)用再次查找的開銷。
  • 3,如果在該類的方法列表中沒找到對應(yīng)的IMP,在通過該類結(jié)構(gòu)中的super_class指針在其父類結(jié)構(gòu)的方法列表中去查找,直到在某個父類的方法列表中找到對應(yīng)的IMP,返回它,并加入cache中;
  • 4,如果在自身以及所有父類的方法列表中都沒有找到對應(yīng)的 IMP,則看是不是可以進行動態(tài)方法決議(后 面有專文講述這個話題);
  • 5,如果動態(tài)方法決議沒能解決問題,進入下面要講的消息轉(zhuǎn)發(fā)流程续挟。便利函數(shù):我們可以通過 NSObject 的一些方法獲取運行時信息或動態(tài)執(zhí)行一些消息:

class 返回對象的類:

isKindOfClass,isMemberOfClass 檢查對象是否在指定的類繼承體系中;
respondsToSelector 檢查對象能否相應(yīng)指定的消息;
conformsToProtocol 檢查對象是否實現(xiàn)了指定協(xié)議類的方法;
methodForSelector 返回指定方法實現(xiàn)的地址紧卒;
performSelector:withObject 執(zhí)行 SEL 所指代的方法

OC做為一門面向?qū)ο笳Z言,自然具有面向?qū)ο蟮恼Z言特性诗祸,如封裝跑芳、繼承轴总、多態(tài)。他具有靜態(tài)語言的特性(如C++)博个,又有動態(tài)語言的效率(動態(tài)綁定怀樟、動態(tài)加載等)。整體來說坡倔,確實是一門不錯的編程語言漂佩。


OC的動態(tài)語言特性

現(xiàn)在,讓我來想想OC的動態(tài)語言特性罪塔。OC的動態(tài)特性表現(xiàn)為了三個方面:
動態(tài)類型投蝉、動態(tài)綁定動態(tài)加載征堪。
之所以叫做動態(tài)瘩缆,是因為必須到運行時(runtime)才會做一些事情。

(1)動態(tài)類型

動態(tài)類型佃蚜,說簡單點就是id類型庸娱。動態(tài)類型是跟靜態(tài)類型相對的。像內(nèi)置的明確的基本類型都屬于靜態(tài)類型(int谐算、NSString等)熟尉。靜態(tài)類型在編譯的時候就能被識別出來。所以洲脂,若程序發(fā)生了類型不對應(yīng)斤儿,編譯器就會發(fā)出警告。而動態(tài)類型就編譯器編譯的時候是不能被識別的恐锦,要等到運行時(runtime)往果,即程序運行的時候才會根據(jù)語境來識別。所以這里面就有兩個概念要分清:編譯時跟運行時一铅。

(2)動態(tài)綁定

動態(tài)綁定(dynamic binding)貌似比較難記憶陕贮,但事實上很簡單,只需記住關(guān)鍵詞@selector/SEL即可潘飘。先來看看“函數(shù)”肮之,對于其他一些靜態(tài)語言,比如c++,一般在編譯的時候就已經(jīng)將將要調(diào)用的函數(shù)的函數(shù)簽名都告訴編譯器了卜录。靜態(tài)的局骤,不能改變。而在OC中暴凑,其實是沒有函數(shù)的概念的峦甩,我們叫“消息機制”,所謂的函數(shù)調(diào)用就是給對象發(fā)送一條消息。這時凯傲,動態(tài)綁定的特性就來了犬辰。OC可以先跳過編譯,到運行的時候才動態(tài)地添加函數(shù)調(diào)用冰单,在運行時才決定要調(diào)用什么方法幌缝,需要傳什么參數(shù)進去。這就是動態(tài)綁定诫欠,要實現(xiàn)他就必須用SEL變量綁定一個方法涵卵。最終形成的這個SEL變量就代表一個方法的引用。這里要注意一點:SEL并不是C里面的函數(shù)指針荒叼,雖然很像轿偎,但真心不是函數(shù)指針。SEL變量只是一個整數(shù)被廓,他是該方法的ID坏晦。以前的函數(shù)調(diào)用,是根據(jù)函數(shù)名嫁乘,也就是字符串去查找函數(shù)體昆婿。但現(xiàn)在,我們是根據(jù)一個ID整數(shù)來查找方法蜓斧,整數(shù)的查找字自然要比字符串的查找快得多仓蛆!所以,動態(tài)綁定的特定不僅方便挎春,而且效率更高看疙。

(3)動態(tài)加載

動態(tài)加載就是根據(jù)需求動態(tài)地加載資源。我對動態(tài)加載比較陌生搂蜓,所以就沒什么可總結(jié)的啦狼荞。等以后慢慢完善辽装。

寫在最后

我得寫作原則:
在技術(shù)學習道路上帮碰,閱讀量和代碼量絕不能線性提升你的技術(shù)水平。
同樣寫文章也是如此拾积,作者所寫的文章完全是基于自己對技術(shù)的理解殉挽,在寫作時也力求形象不抽象。絕不copy充數(shù)拓巧,所以也歡迎大家關(guān)注和參與討論斯碌。
技術(shù)學習絕不能孤膽英雄獨闖天涯,而應(yīng)在一群人的交流碰撞肛度,享受智慧火花的狂歡傻唾。
希望我的文章能成為你的盛宴,也渴望你的建議能成為我的大餐。
如有錯誤請留言指正冠骄,對文章感興趣可以關(guān)注作者不定期更新伪煤。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市凛辣,隨后出現(xiàn)的幾起案子抱既,更是在濱河造成了極大的恐慌,老刑警劉巖扁誓,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件防泵,死亡現(xiàn)場離奇詭異,居然都是意外死亡蝗敢,警方通過查閱死者的電腦和手機捷泞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來前普,“玉大人肚邢,你說我怎么就攤上這事∈们洌” “怎么了骡湖?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長峻厚。 經(jīng)常有香客問我响蕴,道長,這世上最難降的妖魔是什么惠桃? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任浦夷,我火速辦了婚禮,結(jié)果婚禮上辜王,老公的妹妹穿的比我還像新娘劈狐。我一直安慰自己,他們只是感情好呐馆,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布肥缔。 她就那樣靜靜地躺著,像睡著了一般汹来。 火紅的嫁衣襯著肌膚如雪续膳。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天收班,我揣著相機與錄音坟岔,去河邊找鬼。 笑死摔桦,一個胖子當著我的面吹牛社付,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼鸥咖,長吁一口氣:“原來是場噩夢啊……” “哼纪隙!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起扛或,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤绵咱,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后熙兔,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體悲伶,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年住涉,在試婚紗的時候發(fā)現(xiàn)自己被綠了麸锉。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡舆声,死狀恐怖花沉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情媳握,我是刑警寧澤碱屁,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站蛾找,受9級特大地震影響娩脾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜打毛,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一柿赊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧幻枉,春花似錦碰声、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至罗珍,卻和暖如春洽腺,著一層夾襖步出監(jiān)牢的瞬間脚粟,已是汗流浹背覆旱。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留核无,地道東北人扣唱。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親噪沙。 傳聞我的和親對象是個殘疾皇子炼彪,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,726評論 0 9
  • 消息發(fā)送和轉(zhuǎn)發(fā)流程可以概括為:消息發(fā)送(Messaging)是 Runtime 通過 selector 快速查找 ...
    lylaut閱讀 1,855評論 2 3
  • 目錄 Objective-C Runtime到底是什么 Objective-C的元素認知 Runtime詳解 應(yīng)用...
    Ryan___閱讀 1,939評論 1 3
  • 本文分為4個部分 1.介紹OC和C語言之間的轉(zhuǎn)換 2.介紹運行時和相關(guān)術(shù)語 3.介紹消息發(fā)送機制已及怎樣找到函數(shù)實...
    一片楓葉隨風舞閱讀 309評論 0 1
  • 最近經(jīng)常出差正歼,發(fā)現(xiàn)一個很有意思的現(xiàn)象辐马,同行的小姑娘,輕裝簡行局义。反而是年紀偏大一些的喜爷,隨身拿著水杯,拖著大大的...
    雨榭風亭閱讀 570評論 0 0