“好記性不如爛筆頭”闷串,雖然我不是用的筆祖搓。但是敲一遍對我來說效果肯定比單純的看一遍效果更好S狻!拯欧!
在對象上調(diào)用方法是OC中經(jīng)常使用的功能详囤,用OC的術(shù)語來說這叫做“傳遞消息”。消息有“名稱(name)”和“選擇子(selector)”镐作,可以接受參數(shù)藏姐,也可以返回值。
OC是C的超集该贾,C語言使用“靜態(tài)綁定”羔杨,也就是說在編譯期就能決定運(yùn)行時(shí)所應(yīng)調(diào)用的函數(shù)。如下代碼:
如果不考慮內(nèi)聯(lián)(inline)杨蛋,那么編譯器在編譯代碼的時(shí)候就已經(jīng)知道程序中有printHello和printGoodbye這兩個(gè)函數(shù)了兜材,于是會直接生成調(diào)用這些函數(shù)的指令。而函數(shù)地址實(shí)際上是硬編碼在指令之中的逞力。若是將剛才那段代碼寫成下面這樣曙寡,就得使用“動態(tài)綁定”了。
編譯器在這種情況下生成的指令和圖1中的不同寇荧。
在OC中如果向某個(gè)對象傳遞消息卵皂,那就會使用動態(tài)綁定機(jī)制來決定需要調(diào)用的方法。在底層砚亭,所有方法都是普通的C函數(shù)灯变,然而對象受到消息之后,究竟該調(diào)用哪個(gè)方法則完全在運(yùn)行期決定捅膘,甚至可以在程序運(yùn)行時(shí)改變添祸,這些特性使得OC成為了一門真正的動態(tài)語言。
在OC中給對象發(fā)送消息可以這樣來寫:
someObj是接收者(receiver)寻仗,messageName叫做選擇子(selector)刃泌,選擇子和參數(shù)合起來稱為“消息(message)”。編譯器看到此消息后署尤,將其轉(zhuǎn)換為一條標(biāo)準(zhǔn)的C語言函數(shù)調(diào)用耙替,所調(diào)用的函數(shù)乃是消息傳遞機(jī)制中的核心函數(shù),叫做objc_msgSend曹体,其原型如下:
這是個(gè)參數(shù)可變的函數(shù)俗扇,能接受兩個(gè)和兩個(gè)以上的參數(shù),第一個(gè)參數(shù)代表接收者箕别,第二個(gè)參數(shù)是選擇子(SEL是選擇子的類型)铜幽,后續(xù)的參數(shù)就是消息中的那些參數(shù),其順序不變串稀。編譯器會把圖3中的消息轉(zhuǎn)換成如下函數(shù):
objc_msgSend函數(shù)會依據(jù)接收者與選擇子的類型來調(diào)用適當(dāng)?shù)姆椒ǔ住榱送瓿纱瞬僮鳎摲椒ㄐ枰诮邮照咚鶎俚念愔兴褜て洹胺椒斜怼蹦附兀绻苷业脚c選擇子名稱相符的方法到忽,就調(diào)至其實(shí)現(xiàn)代碼。如果找不到就沿著繼承體系繼續(xù)向上查找清寇,等找到合適的方法之后再跳轉(zhuǎn)喘漏。如果最終都找不到,那就執(zhí)行“消息轉(zhuǎn)發(fā)”操作颗管。
當(dāng)找到相符的方法之后陷遮,objc_msgSend會將匹配結(jié)果緩存在“快速映射表”里,每個(gè)類都會有這么一塊緩存垦江,如果稍后還向該類發(fā)送此消息帽馋,那么執(zhí)行起來就會很快了。
上面所敘述的只是部分消息的調(diào)用過程比吭,其實(shí)還有一些其他的“邊界情況”绽族,這時(shí)候需要交由OC運(yùn)行環(huán)境中的另外一些函數(shù)來處理:
1、objc_msgSend_stret:如果待發(fā)送的消息要返回結(jié)構(gòu)體衩藤,那么可交由此函數(shù)處理吧慢。只有當(dāng)CPU的寄存器能夠容納得下消息返回類型時(shí),這個(gè)函數(shù)才能處理此消息赏表。假如無法容納的時(shí)候(比如結(jié)構(gòu)體太大),那么由另一個(gè)函數(shù)執(zhí)行派發(fā)检诗。此時(shí)匈仗,那個(gè)函數(shù)會通過分配在棧上的某個(gè)變量來處理消息所返回的結(jié)構(gòu)體。
2逢慌、objc_msgSend_fpret:如果消息返回的是浮點(diǎn)數(shù)....悠轩。書上說這個(gè)函數(shù)是為了處理x86等架構(gòu)CPU中某些令人稍覺驚訝的奇怪狀況。
3攻泼、objc_msgSendSuper:給超類發(fā)消息火架,如[super message:paramater],那么就需要此函數(shù)來處理
猜測:stret->struct return忙菠、fpret->float point return
剛才提到何鸡,objc_msgSend等函數(shù)一旦找到應(yīng)該調(diào)用的方法實(shí)現(xiàn)之后,就會“跳轉(zhuǎn)過去”牛欢。之所以能這樣做骡男,是因?yàn)镺C對象的每個(gè)方法可以視為簡單的C函數(shù),其原型如下:
注:書中說“真正的函數(shù)名和上面寫的可能不太一樣氢惋,筆者用類(Class)和選擇子(selector)來命名是想解釋其工作原理”
每個(gè)類都有一張表格洞翩,表格里面存的都是指針,每個(gè)指針都會指向這種函數(shù)焰望,而選擇子的名稱則是查表時(shí)候所使用的“鍵”骚亿。objc_messageSend等函數(shù)正是通過這張表格來尋找應(yīng)該執(zhí)行的方法并跳轉(zhuǎn)到其實(shí)現(xiàn)的。圖6中原型的樣子和objc_messageSend很像熊赖,這不是巧合来屠,而且為了利用“尾調(diào)用優(yōu)化”技術(shù),令“跳轉(zhuǎn)到方法的實(shí)現(xiàn)”更加簡單一些震鹉。