Runtime
Runtime 就是去解決如何在運行時期找到調(diào)用方法。
(OC 是一門動態(tài)語言臊诊,函數(shù)調(diào)用變成了消息發(fā)送,在編譯期不能知道要調(diào)用哪個函數(shù))
對于實例變量有如下的思路:
instance -> class -> method -> SEL -> IMP -> 實現(xiàn)函數(shù)
實例對象 中存放 isa 指針 以及 實例變量。
有 isa 指針 可以找到 實例對象 所屬的類對象 (類也是對象蠢笋,面向?qū)ο笾幸磺卸际菍ο?。
- 類 中存放著 實例方法列表鳞陨,在這個 方法列表 中 SEL 作為 key昨寞,IMP 作為 value瞻惋。(在編譯時期,根據(jù)方法名字會生成一個唯一的 Int 標識援岩,這個標識就是 SEL歼狼。IMP 其實就是函數(shù)指針 指向了最終的函數(shù)實現(xiàn)。)
- 整個 Runtime 的核心就是 objc_msgSend 函數(shù)享怀,通過給類發(fā)送 SEL 以傳遞消息羽峰,找到匹配的 IMP 再獲取最終的實現(xiàn)。
- 類中的 super_class 指針 可以追溯 整個繼承鏈添瓷。
- metaClass是元類梅屉,也有 isa 指針、super_class 指針鳞贷。其中保存了 類方法列表坯汤。
重點:
- 向一個對象發(fā)送消息時,Runtime 會根據(jù) 實例對象 的 isa 指針 找到 其所屬的類搀愧,并自底向上直至根類(NSObject)中 去尋找 SEL 所對應的方法惰聂,找到后就運行整個方法。
SEL 與 IMP
SEL 可以將其理解為方法的 ID妈橄。
IMP 可以理解為函數(shù)指針庶近,指向了最終的實現(xiàn)。
- others
OC 中不支持函數(shù)重載的原因:就是因為一個類的方法列表中不能存在兩個相同的 SEL 眷蚓。
但是多個方法卻可以在不同的類中有一個相同的 SEL鼻种,不同類的實例對象執(zhí)行相同的 SEL 時,會在各自的方法列表中去根據(jù) SEL 去尋找自己對應的IMP沙热,這使得OC可以支持函數(shù)重寫叉钥。
消息傳遞機制
- objc_msgSend函數(shù)的消息處理過程
- 不涵蓋消息cache機制
- 需要對Objective-C runtime有一定的了解
如下用于描述 objc_msgSend 函數(shù)的調(diào)用流程:
1.檢測 SEL 是否應該被忽略;
2.檢測發(fā)送的 target 是否為 nil 篙贸,如果是則忽略該消息投队;
- 當調(diào)用實例方法時,通過 isa 指針找到實例對應的 class 并且在其中的緩存方法列表以及方法列表中進行查詢爵川,如果找不到則根據(jù) super_class 指針在父類中查詢敷鸦,直至根類(NSObject 或 NSProxy).
- 當調(diào)用類方法時,通過 isa 指針找到實例對應的 metaclass 并且在其中的緩存方法列表以及方法列表中進行查詢寝贡,如果找不到則根據(jù) super_class 指針在父類中查詢扒披,直至根類(NSObject 或 NSProxy). (根據(jù)此前的開篇中的圖,Root Meta Class 還是有根類的圃泡。)
- 如果還沒找到則進入消息動態(tài)解析過程碟案。
動態(tài)消息解析過程
如下用于描述動態(tài)消息解析的流程:
通過 resolveInstanceMethod 得知方法是否為動態(tài)添加,YES則通過 class_addMethod 動態(tài)添加方法颇蜡,處理消息价说,否則進入下一步辆亏。(dynamic 屬性就與這個過程有關(guān),當一個屬性聲明為 dynamic 時 就是告訴編譯器:開發(fā)者一定會添加 setter/getter 的實現(xiàn)鳖目,而編譯時不用自動生成扮叨。)
這步會進入 forwardingTargetForSelector 用于指定哪個對象來響應消息。如果返回nil 則進入第三步领迈。(這種方式把消息原封不動地轉(zhuǎn)發(fā)給目標對象甫匹,有著比較高的效率。如果不能自己的類里面找到替代方法惦费,可以重載這個方法,然后把消息轉(zhuǎn)給其他的對象抢韭。)
這步調(diào)用 methodSignatureForSelector 進行方法簽名薪贫,這可以將函數(shù)的參數(shù)類型和返回值封裝。(如果返回 nil 說明消息無法處理并報錯 unrecognized selector sent to instance刻恭,如果返回 methodSignature瞧省,則進入 forwardInvocation )在forwardInvocation這里可以修改實現(xiàn)方法,修改響應對象等(如果方法調(diào)用成功鳍贾,則結(jié)束鞍匾。如果依然不能正確響應消息,則報錯 unrecognized selector sent to instance.)
(可以利用 2骑科、3 中的步驟實現(xiàn)對接受消息對象的轉(zhuǎn)移橡淑,可以實現(xiàn)“多重繼承”的效果。)