OC底層原理十二: objc_msgSend(方法快速查找)

OC底層原理 學(xué)習(xí)大綱

上一節(jié)已了解類的cache結(jié)構(gòu)插入操作柴梆。但是有幾個(gè)問題:

  • 1. 何時(shí)插入緩存偷办?
  • 2. 緩存讀取機(jī)制是怎樣盆赤?
cache分析流程

現(xiàn)在開始探索之旅

1. 探索插入操作
2. 介紹Runtime
3. 了解方法的本質(zhì)
4. objc_msgSend解析

1. 探索插入操作

我們從insert開始尋找誰(shuí)在調(diào)用它

  • objc4源碼下搜索->insert(c++的調(diào)用方式->
image.png
  • 發(fā)現(xiàn)cache_fill調(diào)用了它搓译,我們繼續(xù)搜索cache_fill:
image.png

我們發(fā)現(xiàn)措伐,在緩存寫入之前烙懦,我們需要先知道:

  • 給誰(shuí)進(jìn)行寫入objc_msgSend
  • 寫入內(nèi)容是什么cache_getImp

到這里铺韧,我們必須引出OC非常重要的機(jī)制:Runtime運(yùn)行時(shí)

2. 介紹Runtime

?? Objective-C Runtime Programming Guide,此文檔不再更新蟹略,適合初步了解Runtime

2.1 什么是Runtime

  • Runtime是一個(gè)由C登失、C++匯編混合開發(fā)的API庫(kù)挖炬,它將程序的一些決定性工作編譯器推遲到運(yùn)行期揽浙,使得OC語(yǔ)言具備動(dòng)態(tài)特性。內(nèi)部使用消息機(jī)制進(jìn)行通信意敛。

2.2 什么是運(yùn)行時(shí)? 什么是編譯時(shí)馅巷?

  • 編譯時(shí):編譯器將源代碼翻譯成機(jī)器識(shí)別代碼。這是一個(gè)靜態(tài)操作草姻,并不會(huì)把代碼寫入內(nèi)存中進(jìn)行運(yùn)行钓猬。
  • 編譯過(guò)程中,會(huì)分析語(yǔ)法是否正確撩独。
  • 編譯時(shí)提示的error敞曹、warning都是編譯時(shí)錯(cuò)誤
  • 編譯過(guò)程檢查就叫編譯時(shí)類型檢查靜態(tài)類型檢查
  • 運(yùn)行時(shí): 將代碼裝載內(nèi)存中账月,讓代碼運(yùn)行起來(lái)
  • 代碼在裝載內(nèi)存之前,只是個(gè)"死家伙"澳迫,靜靜地趴在磁盤中局齿。只有載入內(nèi)存,才是"活的"橄登。
  • 運(yùn)行時(shí)類型檢查與前面所說(shuō)的編譯時(shí)類型檢查(或叫靜態(tài)類型檢查)不一樣抓歼,它不是簡(jiǎn)單的掃描代碼,而是在內(nèi)存中做些操作拢锹,做些判斷谣妻。(是動(dòng)態(tài)活動(dòng)的)

例如一個(gè)函數(shù),只聲明面褐,未實(shí)現(xiàn)拌禾。 command+B編譯時(shí)不會(huì)報(bào)錯(cuò),但是command+R運(yùn)行時(shí)會(huì)報(bào)錯(cuò)展哭。

image.png

2.3 Runtime版本

  • Runtime有兩個(gè)版本湃窍, Legacy(早期版本) 和Modern(現(xiàn)行版本)
  • 早期版本: Objective-C 1.0,用于32位Mac OS X的平臺(tái)上匪傍,實(shí)例變量發(fā)生改變后您市,需要重新編譯其子類
  • 現(xiàn)行版本: Objective-C 2.0役衡,用于iPhone程序和Mac OS X v10.5以后的系統(tǒng)中的 64 位程序茵休,實(shí)例變量發(fā)生改變后,不需要重新編譯其子類手蝎。

2.4 運(yùn)行時(shí)讓OC具備多態(tài)特性

  • OC的運(yùn)行時(shí)機(jī)制:將數(shù)據(jù)類型的確定由編譯時(shí)榕莺,推遲到運(yùn)行時(shí)。OC的這種運(yùn)行時(shí)機(jī)制使對(duì)象的類型對(duì)象的屬性方法運(yùn)行時(shí)才能確定棵介。

  • 多態(tài): 不同對(duì)象以自己的方式響應(yīng)相同的消息的能力叫做多態(tài)

例如:
自然界中的人類(Person)都有一個(gè)相同的方法-sing钉鸯,男人(Man)類屬于人類,女人(Wonan)類也屬于人類邮辽,都繼承了人類后唠雕,會(huì)實(shí)現(xiàn)各自的-sing方法。但是自然界中男人和女人的sing風(fēng)格又不一樣吨述,男人唱的豪邁岩睁,女人唱的委婉,但都繼承了person唱的能力揣云,這就是多態(tài)的現(xiàn)象捕儒。
也就是不同的對(duì)象以自己的方式響應(yīng)相同消息的能力叫多態(tài)。 也可以說(shuō)運(yùn)行時(shí)機(jī)制是多態(tài)基礎(chǔ)邓夕。
描述參考??: 碼上江湖

2.5 Runtime的三種調(diào)用

  • oc代碼調(diào)用刘莹、framework調(diào)用 亿笤、RuntimeAPI調(diào)用


    image.png

2.6 探索runtime

先準(zhǔn)備好靜態(tài)資源的,咱們才能動(dòng)態(tài)起來(lái)栋猖。所以先獲取compiler層文件。

  • 測(cè)試代碼:
@interface HTPerson : NSObject
- (void)sayHello;
@end

@implementation HTPerson
- (void)sayHello{
    NSLog(@"%s",__func__);
}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {

        HTPerson *p  = [HTPerson alloc];
        [p sayHello];
    }
    return 0;
}
  • clang靜態(tài)編譯main.m文件: clang -rewrite-objc main.m -o main.cpp
image.png

我們發(fā)現(xiàn)汪榔,所有OC方法蒲拉,不管是類方法alloc,還是實(shí)例方法sayHello都是調(diào)用了objc_msgSend發(fā)送消息痴腌。

  • 調(diào)用格式: objc_msgSend: (消息接收者, 消息主體)

  • 嘗試手動(dòng)使用objc_msgSend執(zhí)行方法:

  1. 導(dǎo)入頭文件#import <objc/message.h>
  2. 手動(dòng)關(guān)閉運(yùn)行時(shí)的編譯警告: buildSetiing->Enable Strict Checking of objc_msgSend Calls->設(shè)置為No
  3. 加入測(cè)試代碼objc_msgSend(p, sel_registerName("sayHello"));
  4. 打印查看:
image.png

崩潰暫時(shí)不理會(huì)雌团,我們現(xiàn)在發(fā)現(xiàn)sayHello打印成功了

3. 了解方法的本質(zhì)

我們知道,方法的本質(zhì)就是一個(gè)方法名和對(duì)應(yīng)的函數(shù)代碼士聪。

  • OC中锦援,我們使用執(zhí)行對(duì)象+函數(shù)名進(jìn)行函數(shù)調(diào)用 (例如:[person sayHello])。

  • 內(nèi)部完整的調(diào)用流程是: objc_msgSend發(fā)送消息(class sel) -> 通過(guò)sel(方法編號(hào))找到imp(函數(shù)指針地址) -> 找到函數(shù)內(nèi)容

  • 第一步: 發(fā)送消息我們有三種API調(diào)用方法(oc代碼調(diào)用剥悟、framework調(diào)用 灵寺、RuntimeAPI調(diào)用)
  • 第三步:可直接 從函數(shù)指針地址讀取函數(shù)內(nèi)容

上述流程区岗,我們唯一不知道的就是系統(tǒng)如何通過(guò)sel(方法編號(hào))找到imp(函數(shù)指針地址)略板?

4. objc_msgSend解析

了解sel如何找到imp就是探究 objc_msgSend內(nèi)部機(jī)制。

  • 函數(shù)的調(diào)用是極其頻繁的慈缔,所以對(duì)性能的要求非常高叮称。objc_msgSend使用匯編進(jìn)行編寫

  • imp的查找分為2個(gè)階段藐鹤,快速查找(緩存cache中瓤檐,匯編編寫)和慢速查找(方法列表methodTable中,c和c++編寫)娱节,今天先介紹快速查找

接下來(lái)的知識(shí)挠蛉,需要大家先熟悉cache_t的結(jié)構(gòu)

匯編查找函數(shù)的流程: 從指定類開始->定位cache->定位buckets->哈希運(yùn)算獲取首次位置->循環(huán)尋找位置->返回impnull

  • 打開objc4源碼,搜索objc_msgsend括堤,我們選擇arm64真機(jī)環(huán)境進(jìn)行探索(其他環(huán)境也是類似邏輯)碌秸。

  • 找到objc_msgSend入口

image.png
  • 初始化數(shù)據(jù), 從receiver中讀取isa中的悄窃。

    image.png

  • 當(dāng)前objc-msg-arm64.s匯編文件中搜索CacheLookup讥电,找到.macro CacheLookup定義處:

image.png
  • 如果匹配sel成功,調(diào)用Cachehit命中緩存流程, 返回找到的imp
image.png
  • 如果匹配失敗,觸發(fā)CheckMissJumpMiss流程, 告知外部Cache未找到imp

    image.png

  • 未找到imp時(shí)轧抗,進(jìn)入__objc_msgSend_uncached流程恩敌,搜索__objc_msgSend_uncached

image.png
  • 發(fā)現(xiàn)緩存找不到后,進(jìn)入方法列表去查找横媚。 搜索MethodTableLookup
image.png
  • 跳轉(zhuǎn)_lookUpImpOrForward纠炮,進(jìn)入慢速查找階段月趟。

奉上完整流程

image.png

下一節(jié): OC底層原理十三: objc_msgSend(方法慢速查找)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市恢口,隨后出現(xiàn)的幾起案子孝宗,更是在濱河造成了極大的恐慌,老刑警劉巖耕肩,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件因妇,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡猿诸,警方通過(guò)查閱死者的電腦和手機(jī)婚被,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)梳虽,“玉大人址芯,你說(shuō)我怎么就攤上這事〈芫酰” “怎么了谷炸?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)禀挫。 經(jīng)常有香客問我淑廊,道長(zhǎng),這世上最難降的妖魔是什么特咆? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任季惩,我火速辦了婚禮,結(jié)果婚禮上腻格,老公的妹妹穿的比我還像新娘画拾。我一直安慰自己,他們只是感情好菜职,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布青抛。 她就那樣靜靜地躺著,像睡著了一般酬核。 火紅的嫁衣襯著肌膚如雪蜜另。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天嫡意,我揣著相機(jī)與錄音举瑰,去河邊找鬼。 笑死蔬螟,一個(gè)胖子當(dāng)著我的面吹牛此迅,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼耸序,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼忍些!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起坎怪,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤罢坝,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后搅窿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體炸客,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年戈钢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片是尔。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡殉了,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出拟枚,到底是詐尸還是另有隱情薪铜,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布恩溅,位于F島的核電站隔箍,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏脚乡。R本人自食惡果不足惜蜒滩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望奶稠。 院中可真熱鬧俯艰,春花似錦、人聲如沸锌订。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)辆飘。三九已至啦辐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蜈项,已是汗流浹背芹关。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留紧卒,地道東北人充边。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親浇冰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子贬媒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355