在OC中,方法的调用实质上是消息的发送
我们通过clang命令可以查看其调用过程。
1.新建Dog类,
@interface Dog : NSObject
- (void)run;
+ (void)walk;
@end
@implementation Dog
- (void)run{
NSLog(@"%s",__func__);
}
+ (void)walk{
NSLog(@"%s",__func__);
}
@end
2.在main.m中调用Dog方法
Dog * d = [Dog new];
[d run];[Dog walk];
3.在终端中cd到项目根目录,输入
clang -rewrite-objc main.m,回车,会得到main.cpp文件,在main.cpp底部可以看到如下代码:
((void (*)(id, SEL))(void *)objc_msgSend)((id)d, sel_registerName("run"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Dog"), sel_registerName("walk"));
//简洁版
objc_msgSend(d, sel_registerName("run"));
//或者 objc_msgSend(d, @selector(run));
objc_msgSend(objc_getClass("Dog"), sel_registerName("walk"));
//或者objc_msgSend(objc_getClass("Dog"), @selector(walk));
如上,方法的调用是调用objc_msgSend(),传入两个参数:方法接收者和执行的方法名(方法编号)。
objc_msgSend的调用过程大致如下:

可以概括的说:
方法的调用,实质是通过objc_msgSend方法向方法接收者发送与调用的方法同名的消息,然后runtime通过汇编语言查找imp(方法的实现)的过程。
查找imp的过程可以理解为:
如果recevier不存在,直接return,不继续执行;如果recevier存在,通过isa找到recevier的class。
1 先在class的缓存中查找是否存在方法的imp,存在imp,直接返回imp。
2 缓存中不存在,则在class的方法列表中查找。
2.1 在class的方法列表查找的开始,会先查找类的缓存(OC的动态性,可能缓存会动态添加了该方法),缓存存在imp,返回imp;缓存不存在,再从方法列表查找。
2.2 class的方法列表有imp,把imp放到缓存中,方便下次查找,然后返回imp。
2.3 class的方法列表没有imp,循环查找父类
2.3.1 父类的缓存中有imp,则把imp放到类的缓存中,并返回imp。
2.3.2 父类的缓存中没有imp,在其方法列表查找。有imp,则把imp放到类的缓存中,并返回imp;没有imp,则在父类的父类中循环 2.3.1与2.3.2
2.4 以上没找到,尝试方法解析
2.5 没找到,抛出异常:unrecognized selector sent to class xxx