簡介
Objective-C是開發(fā)蘋果軟件的語言, 大部分是C語言.除去一些基本的特性, 最重要的它是一門動態(tài)語言, 其動態(tài)性的基石便是rumtime, 即運(yùn)行時機(jī)制.可以說OC沒有運(yùn)行時就沒有了魅力.
消息機(jī)制
對于靜態(tài)語言, 執(zhí)行方法顯得比較死,方法名就能決定了此次方法執(zhí)行的全部因素, 但是對于消息分發(fā)機(jī)制來說,就很靈活。編譯階段一切都是未定的, 并不知道對象是否能處理, 也不知道最終是哪個對象處理, 也不知道最終調(diào)用的方法實現(xiàn)是什么, 而這一切都是在運(yùn)行時決定.
下面我們來詳細(xì)剖析一下方法調(diào)用的整個的過程
- (void)viewDidLoad {
[super viewDidLoad];
Dog *dog = [[Dog alloc] init];
[dog doSomething];
}
[dog doSomething]
[dog doSomething]
干了什么?
編譯器將
[dog doSomething]
--->
objc_msgSend(dog, @selector(doSomething))
-
objc_msgSend(dog, @selector(doSomething))
干了什么?這是重點(diǎn), 詳細(xì)講解:- 檢查dog是個什么類型的對象,即查找dog的Class, OC對象本身有一個isa成員, 這個isa決定了對象的類型, 即isa指針?biāo)赶虻腃lass是dog的真實類型, 而不在乎用什么類型的指針接收這個對象, 這是多態(tài)的基礎(chǔ).
- 這一步可以玩的花樣:
如果在[dog doSomething]
這句代碼執(zhí)行之前(注意一定要之前), 改變dog的isa指針的指向, 改為Cat
類型, 那么處理這條消息的對象就不是Dog類型了, 而是Cat類型. OC中KVO就是用這種方式來實現(xiàn)的, 可以看我這篇文章KVO的原理, 底層實現(xiàn).
- OC中class維護(hù)一份方法列表
objc_method_list
,
這個列表中存放著SEL, 即相當(dāng)于方法名, 也可以說是方法的id. 這一步系統(tǒng)會查找class中objc_method_list
中有沒有doSomething
, 如果沒有則向其superclass的objc_method_list
中去查找.
- 這一步可以玩的花樣:
同理在這一步之前, 即使沒有顯式的doSomething
方法聲明和實現(xiàn), 只要利用class_addMethod
來動態(tài)添加doSomething
方法即可. 但是需要用到performSelector
相關(guān)的方法來調(diào)用以保證編譯通過.
- 找到
doSomething
這個SEL之后, 系統(tǒng)就會調(diào)用, 注意, 這個SEL仍然不能決定系統(tǒng)最終執(zhí)行的代碼是什么, 決定者是與SEL有映射關(guān)系的IMP, 簡單說每一個SEL只相當(dāng)于一個字典的key, 這個key所對應(yīng)的value才真正決定了方法的實現(xiàn), 這一步只是系統(tǒng)通過SEL去找到這個value, 這個value就是IMP, IMP是一個函數(shù)指針, 指向方法的實現(xiàn), IMP決定了最終調(diào)用哪段代碼.
- 這一步可以玩的花樣:
在函數(shù)執(zhí)行之前, 如果改變了doSomething
這個SEL對應(yīng)的IMP, 改為另一個方法eat
的IMP, 那么這句[dog doSomething]
最終的結(jié)果是dog
執(zhí)行了eat
的實現(xiàn).method_exchangeImplementations
這個函數(shù)便是通過這種方式來做到方法交換的.
從上面可以看到一句編譯階段的[dog doSomething]
基本決定不了任何東西, 在以上運(yùn)行過程中隨意改變一些東西,就可以使[dog doSomething]
"面目全非". 這也是rumtime強(qiáng)大的地方.
覺得有用的的猿友點(diǎn)個贊!