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.m
将MsgSend.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
是怎么确定应该调用哪个方法的呢?
它有一个动态查找过程:
- 在相应对象的缓存方法列表中(
objc_class
的cache
)查找调用的方法 - 如果没有找到,则在相应的对象方法列表中查找调用的方法
- 如果还没找到,就到父类指针指向的对象中执行1、2
- 如果直到根类都没有找到就进行消息转发,给自己保留处理找不到方法这一状况的机会
- 调用
resolveInstanceMethod
,有机会让类添加这个函数的实现 - 调用
forwardingTargetForSelector
,让其他对象执行这个函数 - 调用
forwardInvocation
,更加灵活的处理函数调用 - 如果通过以上操作都没有找到,也没有进行特殊处理,就抛出
doesNotRecognizeSelector
异常
下图(1-4步)表示根据类的层级逐层往上查找方法表中有没有对应的方法
根据类的层级查找方法
有三次机会处理方法找不到的情况。利用这种方式可以让所有方法通过消息转发跟踪到一个类的所有方法的调用。下图表示消息转发机制(5-7步)
消息转发机制
网友评论