对象调用方法,这个在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!