ios面试我爱编程

Runtime应用系列:消息机制

2018-07-26  本文已影响26人  羊非鱼丶

OC中都是通过[MyClass classMethod]调用一个方法。而它的底层实现如何呢?接下来写一个简单的方法调用:

#import "MsgSend.h"

@implementation MsgSend
+ (void)myMethod {
    NSLog(@"This is a class method");
}
@end

int main(int argc, char * argv[]) {
    @autoreleasepool {
        [MsgSend myMethod];
    };
    return 0;
}

接下来将OC代码转为C++代码,看一下具体的底层实现。
使用终端cd到当前项目目录下,使用命令行clang -rewrite-objc MsgSend.mMsgSend.m转为C++代码,执行完毕后项目目录下会生成MsgSend.cpp文件,在cpp文件中找到main函数对应的C++代码

int main(int argc, char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        ((void (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("MsgSend"), sel_registerName("myMethod"));
    };
    return 0;
}

通过以上代码我们可以知道[MsgSend myMethod]被转换成了objc_msgSend的调用,第一个参数objc_getClass("MsgSend")是调用的类,第二个参数是sel_registerName("myMethod")调用的方法。

那么objc_msgSend是怎么确定应该调用哪个方法的呢?
它有一个动态查找过程:

  1. 在相应对象的缓存方法列表中(objc_classcache)查找调用的方法
  2. 如果没有找到,则在相应的对象方法列表中查找调用的方法
  3. 如果还没找到,就到父类指针指向的对象中执行1、2
  4. 如果直到根类都没有找到就进行消息转发,给自己保留处理找不到方法这一状况的机会
  5. 调用resolveInstanceMethod,有机会让类添加这个函数的实现
  6. 调用forwardingTargetForSelector,让其他对象执行这个函数
  7. 调用forwardInvocation,更加灵活的处理函数调用
  8. 如果通过以上操作都没有找到,也没有进行特殊处理,就抛出doesNotRecognizeSelector异常
    下图(1-4步)表示根据类的层级逐层往上查找方法表中有没有对应的方法
    根据类的层级查找方法

有三次机会处理方法找不到的情况。利用这种方式可以让所有方法通过消息转发跟踪到一个类的所有方法的调用。下图表示消息转发机制(5-7步)

消息转发机制
上一篇 下一篇

猜你喜欢

热点阅读