[iOS进阶]iOS消息机制

1、静态绑定和动态绑定的区别

静态绑定,即在编译时就直接将要调用函数的地址写进去,酱紫就直接进入调用函数中,如下代码中,编译器在编译的时候就已经知道程序中有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;
}

2、消息传递机制

在Objective-C中,如果给某对象传递消息,那就会使用动态绑定机制来决定需要调用的方法。如下例所示,someObject叫做receiver,messageName叫做slector,选择子与参数合起来成为“消息”。编译器会将消息转换为消息传递机制中得核心函数:objc_msgSend()。

//给对象发送消息
id returnValue = [someObject messageName:parameter];

//编译器会转换成为
id returnValue = objec_msgSend(someObject, @selector(messageName), parameter);

objc_msgSend()会按照如下流程处理消息,期间做了些许优化处理

  • 快速映射表。每个类都有一块 快速映射表 的缓存,保存着搜寻过的选择子,如果稍后还向该类发送与选择子相同的消息,那么执行起来就很快乐。
  • 尾递归优化。为了避免栈溢出现象,在搜寻过程中采用了尾递归优化,即直接return另外一个函数的调用而不会将其返回值另作它用,编译器会生成调转至另外一个函数所需的指令码,从而避免向调用堆栈中推入新的栈帧。
  • 处理边界情况。有一些专门处理边界情况的函数,如下:

    • objc_msgSend_strct。如果待发送的消息要返回结构体,那么有该函数处理。
    • objc_msgSend_fprct。如果消息返回的时浮点数,那么由该函数处理。
    • objc_msgSendSuper。如果消息要给超类发消息,例如[super message:parameter],那么由该函数处理。

    消息传递机制图
    [iOS进阶]iOS消息机制_第1张图片

3、消息转发机制

消息转发机制分为两个阶段。

  • 第一阶段先征询接受者,所属的类,看其是否能动态添加方法,以处理当前这个“未知的选择自”,即“动态方法解析”。
  • 第二阶段涉及“完整的消息转发机制”,即运行期系统会请求接受者以其他手段来处理与消息相关的方法调用。这个阶段又可以细分为两部:
    首先请接受者看看有没有其他对象能处理此消息。若有,则将消息转发给那个对象,即“备援接受者”。
    否则启动完整的消息转发机制,运行期系统会把与消息有关的全部细节都封装到NSInvocation对象中,在给接受者最后一次机会,令其设法解决当前还未处理的这条消息。

动态方法解析

对象在收到无法解读的消息后,首先将调用其所属类的下列类方法:

+(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

  1. 在objective-C中,定义属性用@property,让编译器自动生成属性的存取方法可用@sythesize,如果不想让编译器自动生成可用@dynamic。
    更多内容请参考:http://blog.csdn.net/a8467562/article/details/7764368 ↩

你可能感兴趣的:(ios,消息机制,进阶)