對象調用方法,這個在Objective-C裏面叫做傳遞信息(passing a message)。信息有名稱,有方法,接收參數,還可能有返回值。
由於Objective-C是C的一個延展,那麼我們首先來看一下C語言中的函數是怎麼一回事兒。
C語言中的函數調用被稱爲static binding,(靜態or靜止)綁定;意味着被調用的函數在編譯時知道。
#import <stdio.h>
void printHello(){
printf("hello");
}
void printByebye(){
printf("byebye");
}
void doOneThing(int type){
if(type==0){
printHello();
}else{
printByebye();
}
}
上述代碼在編譯的時候,printHello與printByebye都是知道的,編譯器發出指定,直接來調用函數。 這些函數的地址已經被有效的硬編程在這些指令中。
換一種方式,如下展示:
#import <stdio.h>
void printHello(){
printf("hello");
}
void printByebye(){
printf("byebye");
}
void doSomething(int type){void (*func)();
if(type==0){
func=printHello;
}else{
func=printByebye;
}
func();
}
如上所示,在這裏,動態綁定被使用上了,因爲要調用哪個函數是不知道的;只有在 runtime時。
上述兩種情形有何區別呢?
編譯器發出指令:
1.在第一種情形下,函數會在 if 和else 語句中都被調用。
2.在第二種情形下,函數只會被調用一次,付出的代價僅是讀取這個函數的地址,而不用將其硬編碼。
動態綁定是一種機制, 方法被調用,當一個信息傳遞給了一個object. 所有的方法都是,對於特定的信息哪一個方法被調用,完全決定於runtime;甚至可以改變,這一機制讓Objective-C 真正意義上dynamic.
如下所示:
id returnValue=[object messageName: parameter];
object :接收者 (信息的接收者)
messageName:parameter (方法selector與參數結合) 這個被稱爲一個信息。
當看這個叫信息時,編譯器將其轉化成標準的C函數,objc_msgSend:
void objc_msgSend(id self,SEL cmd,...)
這是一個可變的函數,接收不同個數的參數。第一個參數是 消息的接收者,第二個參數方法,後面的參數就是 原來信息中的參數形表,按照其出現的順序依次羅列。
上面的:
id returnValue=objc_msgSend(object,@selector(messageName:),parameter);
objc_msgSend函數調用正確的方法,取決於接收者的類型和方法。 爲了做到這些,objc_msgSend首先會在接收者的方法列表中去找這個方法,如果找到,將會跳到這個方法的實現中去;如果沒有找到,objc_msgSend會一路向上找其父類是否有這個方法。如果還是沒有找到方法,message forwarding kicks in. 哈哈!item 12
看起來好像是當一個方法被調用時,有很多事情要做。幸運地是,objc_msgSend 會緩存這個結果,所以在未來給相同的類發消息時,會執行地很快。
fast path -----會慢一些------ statically bound function 調用;
但是如果cach下來了,比靜態綁定的函數慢不了多少。
實際情況下,信息調用派遣在一個應用中並不是瓶頸。如果它是的話,那麼我們還不如去直接用C語言去寫呢,將oc 對象的任何狀態傳遞給它。
前述方法僅適用於某些信息。額外的函數 暴露給了 Objective-C的 runtime 來去處理某些情況:
objc_msgSend_stret
對於某些信息,返回一個struct(結構體)。 這個函數只能處理 適應CPU 寄存器類型 的返回值。
objc_msgSend_fpret
對於那些返回值爲浮點數的信息。一些architectures需要在函數調用之間對浮點寄存器進行特殊處理,意味首objc_msgSend不是足夠好。 這個函數存在的意義是:處理這些奇怪的事情,如x86上面。
objc_msgSendSuper
如果將信息傳給了父類,例如:[super message:parameter];
在上面,我暗指了 objc_msgSend
//todo!