Runtime相關知識點

Runtime知識結構

一、數據結構

數據結構

1.1、objc_object

objc_object

我們平常用的實例對象都是id類型,對應到runtime 中的objc_object。

  • isa_t
    共用體
  • 關于isa操作相關
    • 實例對象根據isa指針獲取類對象
    • 類對象根據isa指針獲取元類對象
  • 弱引用相關
    • 標記一個對象是否曾經有弱引用指針。
  • 關聯對象
    • 設置關聯屬性的一些方法。
  • 內存管理
    • MRC下的retain、release等方法。
    • ARC和MRC下用的autorelease方法。

1.2、objc_class

objc_class

我們平常用的類,對應到runtime中的objc_class。

  • isa_t
    因為繼承于objc_object,所以也有isa指針,用來指向類的元類對象。
  • Class superClass
    父類對象
  • cache_t cache
    方法緩存結構<消息傳遞會有涉及到>
  • class_data_bits_t bits
    bit通過&FAST_DATA_MASK獲取class_rw_t,class_rw_t包含方法、屬性、協議。

問題1:Class這個類是否是一個對象呢?

解釋:
是對象,因為Class對應runtime中的objc_class,objc_class繼承于objc_object,所以被稱為"類對象"。

1.3、isa指針

1.3.1、isa指針的概念

共用體isa_t

問題2:isa指針的含義

解釋:

  • 包含指針型isa非指針型isa
    • 指針型isa:isa的代表Class地址。
    • 非指針型isa:isa的值的部分代表Class地址。
  • 應用場景:
    • 32位架構用指針型的 64位架構用非指針型的。
    • 這是一種提升內存利用的一種手段。
1.3.2、isa指向
  • 關于對象,其指向類對象

    指向類對象

  • 關于類對象,其指向元類對象

    指向元類對象

1.4、cache_t

1.4.1、概念
  • 用于快速查找方法執行函數。
  • 是可增量擴展的哈希表結構。
  • 局部性原理的最佳應用。
1.4.2、結構

cache_t是一個結構體。


cache_t結構

可以理解為:

  • 一個數組中有bucket_t。
  • bucket_t是結構體。
  • bucket_t包含兩個成員變量:key、IMP。
  • key就是我們使用的selector。
  • 根據key + 哈希算法,可以快速查找IMP。

1.5、class_data_bits_t

  • class_data_bits_t主要是對class_rw_t的封裝。
  • class_rw_t代表了類相關的讀寫信息、對class_ro_t的封裝。
  • class_ro_t代表了類相關的只讀信息。

1.5.1、class_rw_t

class_rw_t
  • methods、properties、protocols是runtime動態添加的
    比如:分類中的方法會在運行時添加到methods中。
  • methods、properties、protocols是可讀可寫的
  • methods、properties、protocols是二維數組
  • methods中存放有method_t對象

1.5.2、class_ro_t

class_ro_t
  • name指的是類名
  • ivars指的是成員變量
  • methods、properties、protocols是編譯器內部生成的
  • methods、properties、protocols是只讀
  • methods、properties、protocols是一維數組
  • methods中存放有method_t對象

1.5.3、method_t

method_t
  • SEL name
    方法名稱
  • const char* types
    返回值 + 參數
  • IMP imp
    無類型的函數指針,指向函數體

1.5.3.1、Type Encodings

Type Encodings
  • 不可變的字符指針
  • 結構:返回值 + n個參數

實例分析:
-(void)aMethod;對應的Type Encodings
解析如下:

aMethod解析

  • aMethod的types === v@:
  • v代表返回值是void無返回值
  • @代表對象id
  • :代表SEl(方法)

注意:

  • 這里的第一個參數和第二個參數是固定的,并且是不可變的。
  • 因為對于OC方法的調用或者說消息傳遞,到達runtime層面,都會轉換成objc_msgSend,它的前面兩個參數就是固定的objc_msgSend(obj, sel/@selector(aMethod)/);**。

1.6、整體數據結構

整體數據結構

二、對象、類對象、元類對象

2.1、概念

  • 類對象
    存儲實例方法列表等信息。
  • 元類對象
    存儲類方法列表等信息。

2.2、isa指針指向圖

isa指針指向圖

isa指針

  • instance實例對象 --> Class類對象
  • Class類對象 --> meta元類對象
  • meta元類對象 --> meta元類對象
  • meta元類對象 --> meta元類對象(自己)

superclass指針

  • Rootclass類對象 --> nil
  • Superclass類對象 --> Rootclass類對象
  • Subclass類對象 --> Superclass類對象
  • meta元類對象 --> Rootclass類對象

問題3:類對象、元類對象分別是什么,有什么區別和聯系?

  • 概念
    類對象是存儲實例方法列表等信息。
    元類對象是存儲類方法列表等信息。
  • isa指針指向圖
    類對象的isa指針指向元類對象
    元類對象的isa指針指向根元類對象
    根元類對象的isa指針指向自己

問題4:如果說調用的一個類方法,沒有對應的實現;但是,有同名的實例方法實現,會不會產生崩潰或者實際調用?

  • 由于根meta元類對象superclass指針指向的是根類對象,如果在根元類對象中沒有找到對應的類方法,就會去根類對象中去查找同名的實例方法。

問題5:筆試題

筆試題

解釋:

  • [self class]轉換為objc_msgSend(self, @selector(class))
  • [super class]轉換為objc_msgSendSuper(super,@selector(class))
  • [super class]雖然是super調用,但是super是一個結構體,里面包含一個id類型對象receiver,也就是當前對象
  • [self class]傳遞流程:在緩存中查找,沒有??在類對象中查找,沒有??在父類對象中查找,沒有??在NSObject中查找,有,返回信息。
  • [super class]傳遞流程:直接在父類對象中查找,沒有??在NSObject中查找,有,返回信息。

三、消息傳遞

3.1、函數介紹

void objc_msgSend(void /* id self, SEL op, ... */ )


消息傳遞

備注:

  • 這里有2個固定參數,一個消息接受者(id類型),一個是方法選擇器名稱(SEL類型),其它才是后續方法傳遞的參數。
  • 當前的接受者就是:當前對象

void objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ )

Super
消息傳遞

備注:

  • 這里有2個固定參數,一個是結構體類型指針(objc_super),一個是方法選擇器名稱(SEL類型),其它才是后續方法傳遞的參數。
  • 因為objc_super內部有一個receiver(id類型),就是當前對象;這個super就是編譯器關鍵字。
  • objc_msgSendSuper:是從父類對象的方法列表中開始查找;
    objc_msgSend:是從當前對象緩存列表中查找。

3.2、消息傳遞流程

消息傳遞過程
  • 首先調用方法,查找緩存;如果有,就通過函數指針調用函數,完成方法調用。
    這里的緩存指的是類對象的緩存,是通過哈希查找。

  • 如果緩存中沒有對應方法,就會根據當前實例的isa指針去查找類對象的方法列表;如果有,就通過函數指針調用函數,完成方法調用。
    這里分二分查找一般遍歷查找

  • 如果當前類方法中沒有對應方法,就會逐級父類方法列表中去查找,是通過superclass指針;如果有,就通過函數指針調用函數,完成方法調用。
    這里同樣先去緩存查找,再去方法列表查找。

  • 如果直到根類NSObject,都沒有查到對應方法,就會進入消息轉發流程。

四、消息傳遞三大步驟詳解

4.1、緩存查找

給定值是SEL(方法選擇器),目標值是對應的bucket_t中的IMP

緩存查找函數

  • 從緩存查找對應的實現, 是哈希查找。
  • SEL(方法選擇器)就是key
  • 利用哈希算法,找到bucket_t,從而找到IMP
  • 哈希查找提高了查找效率。

4.2、當前類中查找

  • 對于已排序好的列表,采用二分查找算法查找方法對應執行函數IMP
  • 對于沒有排序的列表,采用一般遍歷查找方法對應執行函數。

4.3、父類逐級查找

父類逐級查找
  • 首先根據superclass指針查找有沒有父類;沒有父類,結束父類查找。
  • 有父類,先去緩存查找;緩存查到了,就返回。
  • 緩存沒有查到,就去方法列表查找;方法列表查到了,就返回。
  • 如果上面兩步驟都沒有查到,就接著查下一個父類。

五、消息轉發

消息轉發

這里主要講的是實例方法的轉發流程。

5.1、(BOOL)resolveInstanceMethod:(SEL)sel

  • 該方法是類方法
  • 參數是方法選擇器(SEL類型)。
  • 返回值是BOOL類型
  • 如果返回值是YES,代表解決了當前實例方法的實現,就結束了消息轉發流程;如果返回值是NO,代表沒有解決,繼續消息轉發

5.2、(id)forwardingTargetForSelector:(SEL)aSelector

  • 該方法是實例方法
  • 參數是方法選擇器(SEL類型)。
  • 返回值是一個對象,相當于指定的轉發目標(自己不處理,讓別人處理)。
  • 如果指定了轉發目標,就結束了消息轉發流程;如果沒有指定轉發目標,繼續消息轉發

5.3、(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector

  • 該方法是實例方法
  • 參數是方法選擇器(SEL類型)。
  • 返回值是一個NSMethodSignature對象,是對一個方法選擇器的返回值類型,參數個數以及參數類型的封裝。
  • 如果返回了方法簽名,則繼續調用forwardInvocation;否則就被標記為消息無法處理
    也就是常見的錯誤:unrecognized selector sent to instance 0x600000da1530

5.4、(void)forwardInvocation:(NSInvocation *)anInvocation

  • 該方法是實例方法
  • 參數是NSInvocation。
  • 無返回值。
  • 如果這里面能夠處理這條消息,消息轉發流程就結束了。

六、Method-Swizzling

Method-Swizzling

主要代碼如下:

//獲取test方法
Method test = class_getInstanceMethod(self, @selector(test));
//獲取otherTest方法
Method otherTest = class_getInstanceMethod(self, @selector(otherTest));
//交換兩個方法的實現
method_exchangeImplementations(test, otherTest);

七、動態添加方法

class_addMethod(<#Class  _Nullable __unsafe_unretained cls#>, <#SEL  _Nonnull name#>, <#IMP  _Nonnull imp#>, <#const char * _Nullable types#>)
  • 第一個參數:為哪個類添加方法。
  • 第二個參數:方法選擇器名稱(SEL類型)。
  • 第三個參數:函數指針
  • 第四個參數:Type Encodings。

八、動態方法解析

問題6、你是否使用過@dynamic關鍵字?

解釋:
也是借此考察運行時內容,因為使用@dynamic實際上是告訴編譯器,自己來實現setter與getter方法,不自動生成。
這樣聲明的屬性,即使你沒有實現setter與getter方法,編譯時,是不會報錯的;但是當你使用到setter與getter方法時(運行時),就會報錯。

問題7:編譯時語言與運行時語言的區別?

解釋:

  • 動態運行時,語言將函數決議推遲到運行時
  • 編譯時,語言在編譯期進行函數決議

問題8: [obj foo]和obj_msgSend()函數之間有什么關系?

解釋:
在經過編譯器轉換之后,就變成了obj_msgSend(obj,@selector(foo)),然后就開始了消息傳遞過程。

問題9:runtime如何通過Selector找到對應的IMP地址的?

解釋:
考察的是消息傳遞機制
查找當前實例對應類對象的緩存??查找類對象的方法列表??逐級查找父級方法列表。

問題10:能否向編譯后的類增加實例變量?

解釋:
考察的是runtime數據結構。
因為編譯后的類,它已經完成了實例變量的布局,這個實例變量是存放在class_ro內部,這個是只讀的,所以不能向編譯后的類增加實例變量。
但是,可以向動態添加的類中添加實例變量。

問題11:函數調用與消息傳遞有怎樣的區別?

解釋:
考察消息傳遞機制

問題12:當我們調用一個方法沒有實現的時候,系統是如何為我們實現消息轉發過程的?

解釋:
考察消息傳遞機制

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,197評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,415評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,104評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,884評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,647評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,130評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,208評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,366評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,887評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,737評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,939評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,478評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,174評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,586評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,827評論 1 283
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,608評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,914評論 2 372

推薦閱讀更多精彩內容