Introduction
The Objective-C language defers as many decisions as it can from compile time and link time to runtime. Whenever possible, it does things dynamically. This means that the language requires not just a compiler, but also a runtime system to execute the compiled code.
OC是一種面向?qū)ο蟮膭討B(tài)語言知残,動態(tài)語言就是在運行時來執(zhí)行靜態(tài)語言的編譯鏈接的工作。這就要求除了編譯器之外還要有一種運行時系統(tǒng)來執(zhí)行編譯等功能粤蝎。OC中這個系統(tǒng)就是runtime赋除。
- 編譯時間指編譯程序?qū)⒃闯绦蚓幾g成目標程序所占用的時間。
- 源程序戳护,是指未經(jīng)編譯的金抡,按照一定的程序設(shè)計語言規(guī)范書寫的,人類可讀的文本文件腌且。通常由高級語言編寫梗肝。
- 目標程序,又稱為“目的程序”铺董,為源程序經(jīng)編譯可直接被計算機運行的機器碼集合巫击,在計算機文件上以.obj作擴展名。目標代碼盡管已經(jīng)是機器指令,但是還不能運行喘鸟,因為目標程序還沒有解決函數(shù)調(diào)用問題匆绣,需要將各個目標程序與庫函數(shù)連接,才能形成完整的可執(zhí)行程序什黑。
Messaging
這一章描述了:
- 消息表達式(message expression)如何轉(zhuǎn)換為objc_msgSend函數(shù)調(diào)用崎淳。
- 如何通過名稱(name)來引用方法(methods)。
- 說明如何充分利用objc_msgSend愕把。
- 如何規(guī)避(circumvent)動態(tài)綁定(dynamic binding)拣凹。
The objc_msgSend Function
[receiver message]
在OC中,消息(message)在運行時(runtime)才被綁定到方法實現(xiàn)恨豁。編譯器(compiler)把消息轉(zhuǎn)換(convert to)為函數(shù)objc_msgSend的調(diào)用嚣镜。objc_msgSend將接收方(receiver)和消息中提到的方法的名稱(即selector)作為函數(shù)的兩個重要的參數(shù):
objc_msgSend(receiver, selector)
消息中所有參數(shù)(arguments)也會傳遞給objc_msgSend函數(shù):
objc_msgSend(receiver, selector, arg1, arg2, ...)
objc_msgSend為動態(tài)綁定執(zhí)行所需的一切。
- 首先橘蜜,它查找selector引用的實現(xiàn)方法菊匿。(因為相同的方法可以被不同的類實現(xiàn),所以定位精確的實現(xiàn)方法取決于receiver的的類型)
- 然后计福,調(diào)用實現(xiàn)方法跌捆,把receiver和方法指定的參數(shù)傳遞給它。
- 最后象颖,把實現(xiàn)方法的返回值作為它的返回值佩厚。
note:編譯器生成對objc_msgSend的調(diào)用,你不要直接在代碼中直接調(diào)用它
消息傳遞的關(guān)鍵依賴于編譯器為每個類和對象創(chuàng)建的結(jié)構(gòu)(structures)说订,每個類結(jié)構(gòu)包含兩個基本(essential)元素:
- 一個指向父類的指針(A point to the superclass)
- 一個類的調(diào)度表(A class dispatch table)抄瓦。這個表擁有將方法選擇器(method selector)與特定類的方法地址相關(guān)聯(lián)的條目(entries)。例如:setOrigin::的selector與setOrigin::的實現(xiàn)方法(implementation)地址關(guān)聯(lián)起來陶冷。
當(dāng)創(chuàng)建一個新對象時钙姊,內(nèi)存會為它分配空間,并且它的實例變量會被初始化埃叭。這個對象的所有變量中第一個是isa指針(指向它的類structure)摸恍,object可以根據(jù)isa訪問它的類和父類。
當(dāng)一個objc_msgSend發(fā)送給一個對象時赤屋,沿著isa查找類結(jié)構(gòu)中調(diào)度表(dispatch table)中的selector對應(yīng)的實現(xiàn)方法的地址(address)。如果沒有找到對應(yīng)的selector壁袄,則繼續(xù)沿著isa查找父類的dispatch table的selector类早。連續(xù)的失敗(successive failures)導(dǎo)致objc_msgSend遍歷層級嗜逻,直到遍歷到NSObject涩僻。
為了加快消息傳遞速度,runtime系統(tǒng)會緩沖(cache)已經(jīng)使用過的selectors和addresses of methods。每一個類都單獨(seperate)創(chuàng)建cache緩沖繼承(inherited)的和定義的方法逆日。
Using Hidden Arguments
當(dāng)objc_msgSend找到方法實現(xiàn)的地址嵌巷,調(diào)用方法,并把message中的arguments傳遞給它室抽。同時搪哪,也包含兩個隱藏的(hidden)arguments。
- 接受消息的對象(The receiving object)坪圾。
- 方法的選擇器(The selector for the method)晓折。
這兩個參數(shù)為方法實現(xiàn)提供了明確的信息:消息表達式的兩半(即[receiver message])。方法將receiving object引用為self兽泄,將selector引用為_cmd漓概。
Getting a Method Address
void (*setter)(id, SEL, BOOL);
int i;
setter = (void(*)(id, SEL, BOOL))[target methodForSelector:@selector(setFilled:)];
for (i = 0, i < 1000, i++) {
setter(targetList(i), @selector(setFilled:), YES);
}
阻止動態(tài)綁定的唯一方式:獲取方法的地址(addresss),直接調(diào)用它(如果它是一個函數(shù))病梢。這種情況只適合在連續(xù)多次調(diào)用某個方法的時候胃珍,可以減少動態(tài)綁定的消耗。
NSObject的methodForSelector:返回一個方法(method)的指針(pointer)蜓陌,用這個pointer去實現(xiàn)一個方觅彰。methodForSelector:返回的指針必須小心的轉(zhuǎn)換為正確的函數(shù)類型。返回和參數(shù)類型都應(yīng)該包含在轉(zhuǎn)換中护奈。例如:
void (*setter)(id, SEL, BOOL);
int i;
setter = (void(*)(id, SEL, BOOL))[target methodForSelector:@selector(setFilled:)];
接受對象(receiving object)和方法選擇器(method selecotr)缔莲,這兩個參數(shù)(arguments)在方法語法中(method syntax)中是被隱藏的,但是方法作為函數(shù)(function)調(diào)用時霉旗,必須是顯示的(explicit)痴奏。例如:
setter(targetList(i), @selector(setFilled:), YES);
使用methodForSelector:阻止動態(tài)綁定在消息傳遞中節(jié)省了大部分時間,但是這大部分時間只有在以下情況才有意義:某個確定的方法連續(xù)的調(diào)用厌秒。如在for loop读拆。
note:methodForSelector:由Cocoa runtime system提供,不是Objective-C語言的特定鸵闪。