静态绑定,即在编译时就直接将要调用函数的地址写进去,酱紫就直接进入调用函数中,如下代码中,编译器在编译的时候就已经知道程序中有printHello与printGoodbye两个函数,遇事会直接生成调用这些函数的指令。
#import <mtdio.h>
void printHello(){
printf("Hello, world\n");
}
void printGoodbye(){
printf("Goodbye, world\n");
}
void doTheThing(int type){
if(type == 0){
printHello();
} else{
printGoodbye();
}
return 0;
}
动态绑定,即要调用的函数直到运行时才能够确定。如下代码,编译器在这种情况下生成的指令与刚才那个例子不同,在第一个例子中,if与else语句里都有函数调用指令。而在第二个例子中,只有一个函数调用指令,不过带调用的函数地址无法硬编码在指令中,而是要在运行期读取出来。
#import <mtdio.h>
void printHello(){
printf("Hello, world\n");
}
void printGoodbye(){
printf("Goodbye, world\n");
}
void doTheThing(int type){
void (*func)();
if(type == 0){
func = printHello();
} else{
func = printGoodbye();
}
func();
return 0;
}
在Objective-C中,如果给某对象传递消息,那就会使用动态绑定机制来决定需要调用的方法。如下例所示,someObject叫做receiver,messageName叫做slector,选择子与参数合起来成为“消息”。编译器会将消息转换为消息传递机制中得核心函数:objc_msgSend()。
//给对象发送消息
id returnValue = [someObject messageName:parameter];
//编译器会转换成为
id returnValue = objec_msgSend(someObject, @selector(messageName), parameter);
objc_msgSend()会按照如下流程处理消息,期间做了些许优化处理
处理边界情况。有一些专门处理边界情况的函数,如下:
- objc_msgSend_strct。如果待发送的消息要返回结构体,那么有该函数处理。
- objc_msgSend_fprct。如果消息返回的时浮点数,那么由该函数处理。
- objc_msgSendSuper。如果消息要给超类发消息,例如[super message:parameter],那么由该函数处理。
消息转发机制分为两个阶段。
对象在收到无法解读的消息后,首先将调用其所属类的下列类方法:
+(BOOL)resolveInstanceMethod:(SEL)selector
使用这种方法的前提是:相关方法的实现代码已经写好了,只等着运行的时候动态插入在类里面就可以了。此方案长用来实现@dynamic属性1。
当前接受者还有第二次机会能处理未知的选择子,在这一步中,运行期的系统会问它,能不能把这条消息转给其他接受者来处理。
-(id) forwardingTargetForSelector:(SEL)selector
在一个对象内部,可能还有一些列其他对象,该对象可经由此方法将能够处理某选择子的相关内部对象返回。如果没有则返回nil。
如果转发算法已经来到这一步的话,那么唯一能做的就是启用完整的消息转发机制了。首先创建NSInvocation对象,把与尚未处理的那条消息有关的全部细节都封于其中。此对象包括选择子、目标及参数。在触发NSInvocation对象时,会调用下列方法
-(void)forwardInvocation:(NSInvocation*)invocation
实现此方法时,若发现某调用操作不应由本类处理,则需调用超类的同名方法。这样子的话,继承体系中得某个类都有机会处理此调用请求,直至NSObject。如果最后调用了NSObject类的方法,那么该方法还会继而当调用“doesNotRecognizeSelector:”以抛出异常,此异常表明选择子最终未能得到处理。
有关消息机制更详细的文章:http://www.cocoachina.com/ios/20150818/13075.html