在對象上調用方法,術語叫做“傳遞消息”,消息有“名稱”和“選擇器(方法)”,可以接收參數,還可能有返回值。OC是C的超集,C語言使用靜態綁定,在編譯期間就能決定運行時做調用的函數。
#include <stdio.h>void PRintHello() { printf("hello, world!/n");}void printGoodbye() { printf("Goodbye, world!/n");}void doTheThing(int type) { if (type == 0) { printHello(); } else { printGoodbye(); }}編譯器在編譯代碼的時候就知道有printHello和printGoodbye兩個函數了,會直接生成調用這些函數的指令,函數地址實際上是硬編碼在指令之中。#include <stdio.h>void printHello() { printf("hello, world!/n");}void printGoodbye() { printf("Goodbye, world!/n");}void doTheThing(int type) { void (*fnc)(); if (type == 0) { fnc = printHello; } else { fnc = printGoodbye; } fnc();}如果代碼變成這樣,就得使用“動態綁定”,因為所要調用的函數直到運行期才能確定,第一個例子中,if 和 else 中都有函數調用指令,第二個例子中只有一個函數調用指令,待調用的函數地址無法硬編碼在指令中,要在運行期讀取出來。給對象發送消息可以這么寫:
id returnValue = [someObject messageName:parameter];someObject叫做“接受者”,messageName叫做“選擇器”,選擇器與參數合起來叫做“消息”。編譯器將上述語句轉換為C語言函數調用 “objc_msgSend”void objc_msgSend(id self, SEL cmd, ...)這是個“參數個數可變的函數”,能接受兩個或兩個以上的參數。第一個參數代表接受者,第二個參數代表選擇器,后續參數是消息中的參數編譯器會把剛才的例子轉換為如下函數。id returnValue = objc_msgSend(someObject, @selector(messageName:), parameter);objc_msgSend 會在接受者所屬的類中搜尋其“方法列表”,如果找到與“選擇器”名稱相符的代碼就實現,沒找到就延繼承體系向上查找,最終找不到,就實現“消息轉發”。備注:每個類都有一塊緩存“快速映射表”,如果稍后還向該類發送相同的消息,執行就快。
運行環境中一些“邊界情況”,需要其他函數處理 1.objc_msgSend_stret:如果待發送的消息要返回結構體,交給這個函數。 2.objc_msgSend_fpre:如果消息返回的是浮點數,交給這個函數。 3.objc_msgSendSuper:給超類發消息,用這個函數。也有與objc_msgSend_fpre和objc_msgSend_fpre等效的處理超類消息的方法。
OC對象的每個方法都可以看做簡單的C函數,原型如下:
<return_type> Class_selector(id self, SEL _cmd, ...)每個類里都會有一張表格,其中的指針都會指向這種函數,選擇器的名稱則是查表時用的“鍵”,objc_msgSend用這張表來尋找應該執行的方法并跳轉實現。“尾調用優化”:如果函數的最后一項操作是調用另外一個函數,編譯器會生成調轉至另一個函數所用的指令碼。不會向調用堆棧中推入新的“棧幀”。注意:只有當函數的最后一項操作僅僅是調用其他函數而不會將其返回值另做他用時
新聞熱點
疑難解答