我們經(jīng)常會講Objetive-C是動態(tài)語言.那么究竟什么是動態(tài)呢?它和C這樣的靜態(tài)語言究竟什么不同呢?
在Objetive-C里,調(diào)用方法,也叫做消息傳遞(pass a message),消息有名稱(name)和選擇子(selector),可以接受參數(shù),而且可能還有返回值.
由于Objetive-C是C語言的超集,所以,我們先來看下C語言的函數(shù)調(diào)用方式,C語言使用靜態(tài)綁定(static binding),也就是在編譯期就能決所調(diào)用的函數(shù),以下是代碼示例:
void func1(){
printf("call func1");
}
void func2(){
printf("call func2");
}
void doTheThing(int type){
if (type == 0) {
func1();
}else{
func2();
}
}
編譯器在編譯代碼時,已經(jīng)知道程序中func1和func2兩個函數(shù)了,函數(shù)是硬編碼在指令之中,如果把剛才的代碼改成下面這樣,會怎么樣呢?
void func1(){
printf("call func1");
}
void func2(){
printf("call func2");
}
void doTheThing(int type){
void (*func)();
if (type == 0) {
func = func1;
}else{
func = func2;
}
}
這就用到動態(tài)綁定了,因為要調(diào)用的函數(shù)的直到運行時才確定,待調(diào)用的函數(shù)無法硬編碼在指令中.
在Objetive-C中,如果向?qū)ο髠鬟f消息,那就會使用動態(tài)綁定機制來決定要調(diào)用的方法,在底層,所有的方法都是底層C函數(shù),對象收到消息后,調(diào)用哪個方法完全有運行期決定,甚至可以再程序運行時改變,這就是Objetive-C成為動態(tài)語言的原因.
給someobject對象發(fā)送消息可以這樣寫:
id returenValue = [someobject messageName:paramter];
someobject成為接受者(receive), messageName成為選擇子(selector),選擇子和參數(shù)結(jié)合起來成為方法(message),編譯器看到此條消息后,會將其轉(zhuǎn)化為標準的C語言調(diào)用,所調(diào)用的函數(shù)就是消息傳遞機制的核心函數(shù),叫做objc_msgSend,其原型如下:
void objc_msgSend(id self SEL cmd ...);
這是個參數(shù)可變的函數(shù),第一個參數(shù)是接受者,第二個參數(shù)是選擇子,SEL是選擇子類型,后續(xù)參數(shù)就是消息中的那些參數(shù),順序不變,選擇子指的是方法的名字,編譯器會把剛才的消息轉(zhuǎn)化為如下函數(shù):
id returnValue = objc_msgSend(someObject,@selector(messageName:),paramter)
objc_msgSend函數(shù)會根據(jù)接受者和選擇子的類型來調(diào)用適當?shù)姆椒?為了完成此操作,該方法需要在所屬的類中尋找其"方法列表",如果能找到和選擇字方法相同的方法,那么就跳轉(zhuǎn)至此方法,如果找不到,那就沿著繼承體系向上找,等找到合適方法再跳轉(zhuǎn),如果找不到到就要執(zhí)行消息轉(zhuǎn)發(fā)(message forwarding)操作.
如果每次objc_msgSend函數(shù)都這樣執(zhí)行,那么效率會慢不少,所以objc_msgSend會將匹配結(jié)果緩存在快速映射表中,每個類都有一塊這樣的緩存,如果稍后繼續(xù)向該類發(fā)送相同的消息,那么會快很多,雖然仍然比不上靜態(tài)綁定,但已經(jīng)不會拖后腿了.