在Objective-C中, 如果向某對象傳遞消息, 那就會使用動態(tài)綁定機(jī)制來決定需要調(diào)用的方法. 在底層, 所有方法都是普通的C語言函數(shù), 然而對象收到消息之后, 究竟該調(diào)用哪個方法則完全于運(yùn)行期決定, 甚至可以在程序運(yùn)行時改變, 這些特性使得Objective-C 成為一門真正的動態(tài)語言.
id returnValue = [someObject messageName:parameter];
在本例中, someObject叫做"接收者"(receiver), messageName叫做"選擇子"(selector). 選擇子與參數(shù)合起來稱為"消息"(messge). 編譯器看到此消息后, 將其轉(zhuǎn)換為一條標(biāo)準(zhǔn)的C語言函數(shù)調(diào)用, 所調(diào)用的函數(shù)乃是消息傳遞機(jī)制中的核心函數(shù), 叫做 objc_msgSend, 其"原型"(prototype) 如下:
void objc_msgSend(id self, SEL cmd, ...)
第一個參數(shù)代表接收者, 第二個參數(shù)代表選擇子(SEL 是選擇子的類型), 后續(xù)參數(shù)就是消息中的那些參數(shù), 其順序不變
id returnValue = objc_msgSend(someObject, @selector(messageName:), parameter);
objc_msgSend函數(shù)會依據(jù)接收者與選擇子的類型來調(diào)用適當(dāng)?shù)姆椒? 為了完成此操作, 該方法需要在接收者所屬的類中搜尋其"方法列表" (list of methods), 如果能找到與選擇子名稱相符的方法, 就跳至其實(shí)現(xiàn)代碼. 若是找不到, 那就沿著繼承體系繼續(xù)向上查找, 等找到合適的方法之后再跳轉(zhuǎn). 如果最終還是找不到相符的方法, 那就執(zhí)行"消息轉(zhuǎn)發(fā)" (message forwarding) 操作.
調(diào)用一個方法需要很多步驟. objc_msgSend 會將匹配結(jié)果緩存在"快速映射表" (fast map) 里面, 每個類都有這樣一塊緩存, 若是稍后還向該類發(fā)送與選擇子相同的消息, 那么執(zhí)行起來就很快了.這種"快速執(zhí)行路徑" (fast path) 還是不如"靜態(tài)綁定的函數(shù)調(diào)用操作" (statically bound function call) 那樣迅速
objc_msgSend_stret. 如果待發(fā)送的消息要返回結(jié)構(gòu)體, 那么可交由此函數(shù)處理. 只有當(dāng)CPU的寄存器能夠容納得下消息返回類型時, 這個函數(shù)才能處理此消息. 若是返回值無法容納于CPU 寄存器中 (比如說返回的結(jié)構(gòu)體太大了) , 那么就由另一個函數(shù)執(zhí)行派發(fā). 此時, 那個函數(shù)會通過分配在棧上的某個變量來處理消息所返回的結(jié)構(gòu)體.
objc_msgSend_fpret. 如果消息返回的是浮點(diǎn)數(shù), 那么可交由此函數(shù)處理. 在某些架構(gòu)的CPU 中調(diào)用函數(shù)時, 需要對"浮點(diǎn)數(shù)寄存器" (floating-point register) 做特殊處理, 也就是說, 通常所用的 objc_msgSend 在這種情況下并不合適. 這個函數(shù)是為了處理x86等架構(gòu)CPU中某些令人稍覺驚訝的奇怪狀況.
objc_msgSendSuper. 如果要給超類發(fā)消息, 例如[super message:parameter], 那么就交由此函數(shù)處理. 也有別外兩個與objc_msgSend_stret和 objc_msgSend_fpret 等效的函數(shù), 用于處理發(fā)給super 的相應(yīng)消息
每個類里都有一張表格, 其中的指針都會指向這種函數(shù), 而選擇子的名稱則是查表時所用的"鍵". objc_msgSend 等函數(shù)正是通過這張表格來尋找應(yīng)該執(zhí)行的方法并跳至其實(shí)現(xiàn)的.
尾調(diào)用優(yōu)化
如果某函數(shù)的最后一項(xiàng)操作是調(diào)用別外一個函數(shù), 那么就可以運(yùn)用"尾調(diào)用優(yōu)化"技術(shù). 編譯器會生成調(diào)轉(zhuǎn)至另一函數(shù)所需的指令碼, 而且不會向調(diào)用堆棧中推入新的"棧幀" (frame stack). 只有當(dāng)某函數(shù)的最后一個操作僅僅是調(diào)用其他函數(shù)而不會將其返回值另作他用時, 才能執(zhí)行"尾調(diào)用優(yōu)化". 這項(xiàng)優(yōu)化對 objc_msgSend 非常關(guān)鍵, 如果不這么做的話, 那么每次調(diào)用 Objective-C 方法之前, 都需要為調(diào)用 objc_msgSend 函數(shù)準(zhǔn)備 "棧幀", 大家在"棧蹤跡" (stack trace) 中可以看到這種"棧幀". 此外, 若是不優(yōu)化, 還會過早地發(fā)生"棧溢出" (stack overflow) 現(xiàn)象