今天讀了一下Effective Objective-C 2.0 的第11條析二,下面做一下紀錄和理解
靜態(tài)綁定和動態(tài)綁定
書中關(guān)于這個描述給了一個例子:
void do1(int type){
if(type == 0){
printA();
}else{
printB();
}
}
void do2(int type){
void(*func)();
if(type == 0){
func = printA;
}else {
func= printB;
}
func();
}
那么對于函數(shù)do1,就是靜態(tài)綁定妒御,對于函數(shù)do2,就是動態(tài)綁定甘桑,為什么呢茬高?
這個是do1執(zhí)行時的指令瘩欺,我們看到printA和printB的地址被硬編碼在指令調(diào)用中结胀,在編譯期就知道printA和printB所在位置,可以直接過去普监。
這個是do2的執(zhí)行指令贵试,在指令call時,我們無法在編譯后直接知道所要調(diào)用函數(shù)地址凯正,需要通過上面運行時毙玻,指令計算的來,那么這就可以認為是動態(tài)綁定廊散,所謂的動態(tài)也就是運行時淆珊。
objc_msgSend()
當(dāng)我們“調(diào)用”方法時,在OC中稱之為傳遞消息奸汇,對象接收到消息后施符,會去“方法列表“中尋找,本類中找不到則向上找擂找,如果一直找不到戳吝,則進行”消息轉(zhuǎn)發(fā)“。那么OC是如何進行消息傳遞的呢贯涎?
在OC中听哭,所有的方法底層都是C語言的函數(shù)庸汗,當(dāng)我們向一個對象發(fā)送一條消息時闰渔,編譯器會將其轉(zhuǎn)換為一個C函數(shù) objc_msgSend(),這個函數(shù)會動態(tài)幫我們綁定要執(zhí)行的函數(shù)襟齿。
既然這個函數(shù)可以幫我們動態(tài)綁定要執(zhí)行的函數(shù)贞绳,那我們是否可以直接使用它來執(zhí)行函數(shù)呢?當(dāng)然可以洞难,但是要注意:
id objc_msgSend(id self, SEL op, ...)
這個是函數(shù)的原型逞姿,單如果直接使用扫步,編譯器會報錯objc_msgSend(person,@selector(age)) Too many arguments to function call ,expected 0,have 2! why????
其實這個我們在使用時函數(shù)的原型應(yīng)該是這個
void objc_msgSend(void /* id self, SEL op, ... */ )
官方說了妻顶,我們應(yīng)該這么辦:
These functions must be cast to an appropriate function pointer type?before being called
其實我們可以看編譯器是怎么做的酸员,[p age]會編譯成如下形式
((int (*)(id, SEL))(void *)objc_msgSend)((id)p, sel_registerName("age"));
好了就是這樣,轉(zhuǎn)換吧讳嘱。
不是所有的消息傳遞都會變成objc_msgSend,還有一些其他的
use objc_msgSend_stret for some struct return types.
use objc_msgSend_fpret for some float return types.
use objc_msgSend_fp2ret for some float return types.
如果給超類發(fā)送消息幔嗦,還有相應(yīng)的函數(shù)如 obj_msgSendSuper(),當(dāng)然這寫函數(shù)在使用時都需要進行類型轉(zhuǎn)換。
最后書中還提了一下這個“尾調(diào)用優(yōu)化”沥潭,這個可以去看阮一峰的這片文章尾調(diào)用優(yōu)化 - 阮一峰的網(wǎng)絡(luò)日志