runtime總結


Runtime-對象, 類對象,元類對象


根類, RootClass父類是nil, 有子類以及子類的子類

當我們給定一個實例的時候, 由於 這個實例是id類型的,  objc_object數據結構中裏面有個isa成員變量, 所指向的就是這個實例所對應的類對象.同樣的, 子類的父類對應的就是對應的類對象, 同樣根類也是一樣的, 

實例通過isa指針可以找到類對象

最右面一列是根類的元類對象父類的子類的元類對象

對於類對象是objc_class這樣的數據結構,但是因爲他繼承與objc_class, 所以他也有isa指針, 對應的根類的isa指針就指向根類的元類對象

類對象與元類對象的區別和聯繫

實例對象可以通過isa指針來找到類對象, 類對象中存儲方法列表等信息以及類對象可以通過isa指針來找到他的元類對象.從而可以訪問一些類方法列表的信息 還要答出原因

我們的類對象和元類對象都是objc_class數據結構的, 這個數據結構由於繼承與objc_object類型纔有isa指針,進而才能實現實例可以通過isa指針來找到類對象, 進而可以訪問方法列表等信息, 類對象可以通過isa指針來找到他的元類對象, 來訪問一些類方法列表的信息

元類對象也是objc_class類型的數據結構, 在他們中也有isa指針, 元類的isa指針都指向根元類對象, 包括根元類自身, 也是指向他自己, 根源類對象他的superclass指針指向了根類對象, 比如說我們調用了類方法, 查找的過程逐級父類查找, 元類中找不到就會找到類方法的同名實例方法.

如果我們調用的一個類方法, 沒有對應的實現,但是有同名的實例方法的實現,會不會發生crash?產生實際的調用?

由於跟元類對象的superclass指針指向根類對象, 元類中查找不到的情況下, 他就會去實例方法中查找, 如果有同名實例方法, 就會調用

消息傳遞的過程

比如說我們調用了一個實例的方法a, 首先呢, 系統會根據當前實例的isa指針找到他的類對象, 在他的類對象中去遍歷方法列表, 去查找同名的方法實現,如果沒有找到, 會查找父類的方法列表然後呢查找根類的方法列表, 如果沒有查找到,就會走到消息轉發流程.

如果調用類方法

就是通過類對象的指針找到元類對象, 然後順次遍歷方法列表,直到根元類對象, 再到根類對象, 再到nil

區別就在元類對象的superclass指針指向根類對象



消息傳遞大致過程


消息傳遞流程這個問題中

首先要答出緩存是否命中, 當前類方法列表是否命中, 逐級父類方法列表是否命中這三個大點

首先調用一個方法中, 是會先查找緩存, 看看緩存中是否有對應選擇器名稱的方法實現,如果有的話通過函數指針調用函數, 就完成一次消息傳遞, 如果沒有命中的時候, 會根據當前實例的isa指針, 去查找當前類對象的方法列表, 看看是否有同樣名稱的方法, 如果找到的話, 那麼我們 通過函數指針進行調用, 結束消息傳遞流程 , 如果沒有命中的話, 就需要逐級父類方法中查找, 通過當前對象的superclass指針進行查找父類的方法列表, 如果沒有找到就通過父類的superclass一直往上找, 直到nil位置, 如果查找到, 就會根據函數指針, 調用函數實現, 結束消息傳遞流程, 如果找到根類,  如果沒有找到就會進入消息轉發流程, 然後結束消息傳遞的過程

其次針對三個大點講內部

對於緩存查找實際上是哈希查找

對於當前類方法查找, 對於已排序的,採用二分查找, 對於沒有排序的, 採用一般遍歷查找

對於逐級父類查找, 就是一連一連的遍歷每一個父類, 對於每個父類的查找也是整個這個過程






結果都是Phone 沒有Mobile


[self class]會被轉化objc_msgSend函數調用

[super class]會被轉化成objc_msgSendSuper函數調用

後者函數調用者雖然是super, 但是super包着的結構體是一個receiver,receiver是當前對象, 所以無論轉化成哪個函數接受者都是當前對象


首先給定的方法選擇器, 通過一個函數, 找到對應的bucket_t,從而返回imp指針,  也就是哈希查找






消息轉發流程

實例方法

對於實例方法的消息轉發流程, 系統會回調resolveInstanceMethod: 這個方法, 這個方法有一個參數, 參數是方法的選擇器,返回值是bool類型的, 相當於告訴系統要不要解決當前這個實例方法的實現, 他是一個類方法, 不是一個實例方法, 如果返回的是yes, 或者說我們給予方法選擇器對應方法的方法實現, 相當於通知系統當前消息已處理, 結束消息轉發流程, 如果說resolveInstanceMethod:返回是no, 系統會給予我們第二次機會來處理這個消息,這個時候會回調forwardingTargetForSelector:這個函數,方法的參數是方法的選擇器SEL類型,  返回值是一個id, 相當於告訴系統, 這次實例方法的調用應該哪個對象來處理, 轉發對象是誰, 如果指定了一個轉發目標, 會把這條消息給轉發目標, 流程結束.如果第二次處理的時候, 仍然沒有返回一個轉發目標的情況下, 系統會給我們第三次處理這條消息的機會,也是最後一次機會, 系統會調用methodsignatureforSelector :這個方法, 參數是方法的選擇器, 返回值是一個methodsignature這個類, 或者說是一個對象,這個對象實際上是對方法選擇器的返回值的類型,以及參數個數和參數類型的封裝, 此時如果如果返回了一個方法簽名的話, 系統會調用forwardInvocation:, 如果forwardInvocation能處理這個消息的話, 則消息轉發流程結束, 如果methodsignatureforSelector:返回的是一個空, 或者是forwardInvocation無法處理這條消息, 被標記會消息無法處理, 常見的crash未識別選擇器就是走到消息流程的最後這一步驟 ,產生的一個打印結果.


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章