Effective Objective-C 2.0 第11條:理解objc_msgSend的作用

動態綁定

Objective-C是C的超集,C語言使用“靜態綁定”,也就是說在編譯期間就能決定運行時所應調用的函數。

#import <stdio.h>
void printHello() {
}
void printGoodbye() {
}
void doTheThing(int age) {
    if (type == 0)
        pirntHello();
    else
        printGoodbye();
    return;
}

如果不考慮“內聯”,那麼編譯器在編譯代碼的時候就已經知道程序中有printHello與printGoodbye這兩個函數了,函數地址實際是硬編碼在指令之中的。

若doTheThing寫成下列方式

void doTheThing(int type) {
    void (*fnc) ();
    if (type == 0)
        fnc = printHello();
    else
        fnc = printGoodbye();
    fnc();
    return;
}

這時就是使用“動態綁定”了,因爲所有調用的函數直到運行期才能確定。

OC動態性

在OC中,如果對某對象傳遞消息,那就會使用動態綁定機制來決定需要調用的方法。在底層,所有方法都是普通的C語言函數,然而對象收到消息以後,究竟該調用哪個方法則完全於運行期決定,給對象發送消息可以這麼寫:

id returnValue = [someOjbect messageName:parameter];
[接受者(receiver) 選擇子(selector):參數];

編譯器收到此消息後,轉換成爲一條標準的C語言函數調用,所調用的函數乃是消息傳遞機制中的核心函數,叫做objc_msgSend,其原型如下:

void objc_msgSend(id self, SEL cmd, ……)
【參數可變】

第一個參數代表接受者,第二個參數代表選擇子(SEL是選擇子類型),後續參數就是消息中的那些參數。編譯器會把剛纔的消息轉換成:

id returnValue = objc_msgSend(someObject, @selector(messageName:), parameter);

先檢查接收者的“方法列表”,如果能找到與選擇子名稱相符的方法,就跳至其實現代碼,若是找不到就沿着其集成體系繼續向上查找,等找到合適的方法之後跳轉,如果最終還是找不到合適的方法,那就執行“消息轉發”。

objc_msgSend會將匹配結果緩存在“快速隱射表”裏面,每個類都有這麼一塊緩存,若是稍後稍後還向該類發送與選擇子相同的消息,那麼執行就快起來了。

objc_msgSend等函數一旦找到應該調用的方法實現之後,就會“跳轉過去”,之所以這麼做,是因爲OC對象的每個方法都可以視爲簡單的C函數,原型如下:

<return_type> Class_selector(id self, SEL _cmd, ……)

真正的類名和函數名和上面可能不太一樣,用“類”(Class)和“選擇子”(selector)來命名是解釋工作原理,每個類都有一個表格,其中的指針都會指向這種函數,而選擇子的名稱是查表時所使用的“鍵”。objc_msgSend正是通過這張表格來尋找應該執行的方法並跳轉至其實現的,這裏利用“尾調用優化”技術,令“跳轉方法實現”這一操作變得簡單。

如果一個函數的最後一項操作室調用另外一個函數,那麼就可以運用“尾調用優化”技術。編譯器會生成跳轉至另一個函數所需的指令碼,而且不會向調用堆棧中推入新的“棧幀”。只有當函數最後一個操作僅僅是調用其他函數而不會將其返回值另作他用時,才能執行“尾調用優化”技術。

發佈了52 篇原創文章 · 獲贊 6 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章