结论
OC中方法调用的本质就是发送消息
验证结论
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p = [Person new];
[p run];
}
return 0;
}
在main.m
文件中有这么一段代码,这是一段很常见的方法调用,使用clang对main.m
进行编译输出main.cpp
文件。
clang -rewrite-objc main.m -o main.cpp
打开main.cpp
文件拉到最下面,会发现这样一段代码
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
Person *p = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("new"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)p, sel_registerName("run"));
}
return 0;
}
可以看出OC方法调用本质就是调用了objc_msgsend()
函数。
可以引用 #include
再将下面的代码复制到main函数
中
Person *p = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("new"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)p, sel_registerName("run"));
会发现效果和原始的代码效果是一样的,所以OC中方法调用的本质就是发送消息
方法查找流程
可运行源码地址
一. 通过汇编在本类缓存中查找,如果查找到执行,如果没有查找到遍历本类的方法列表,如果找到加入到缓存中并执行,如果没找到会继续查找父类。
二. 父类中流程与第一步相同,一直查找到NSObject
还是没有找到将进行动态决议
void dynamicMethodIMP(id self, SEL _cmd)
{
NSLog(@"%@", NSStringFromSelector(_cmd));
}
// 对象方法
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel == sel_registerName("run")) {
class_addMethod(self, sel, (IMP)dynamicMethodIMP, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
// 类方法
+ (BOOL)resolveClassMethod:(SEL)sel
{
return [super resolveClassMethod:sel];
}
三. 如果没有处理动态决议方法,会进入消息转发流程
// 消息转发对象
// 如果返回会调用转发对象的方法实现,方法调用流程结束
// 如果返回为空 则进行下一步 methodSignatureForSelector
- (id)forwardingTargetForSelector:(SEL)aSelector
{
if (self.methodProxy == nil) {
self.methodProxy = [PersonProxy new];
}
if ([self.methodProxy respondsToSelector:aSelector]) {
return self.methodProxy;
}
return nil;
}
// 方法签名
// 如果处理此方法 会调用 forwardInvocation
// 如果没有处理 系统则会崩溃
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
NSMethodSignature *sig = [PersonProxy instanceMethodSignatureForSelector:@selector(sxj_dealNotRecognizedMessage:)];
return sig;
}
// 此方法可以用于在release模式下异常信息捕捉
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
NSString *dubugInfo = [NSString stringWithFormat:@"[debug] unRecognizedMessage: [%@] send message [%@]", NSStringFromClass(self.class), NSStringFromSelector(anInvocation.selector)];
[anInvocation setSelector:@selector(sxj_dealNotRecognizedMessage:)];
[anInvocation setArgument:&dubugInfo atIndex:2];
[anInvocation invokeWithTarget:[PersonProxy new]];
}
四. 如果消息转发也没有处理 系统会崩溃并打印错误信息
2019-05-07 09:35:23.732253+0800 objc-test[11722:6127710] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Person run]: unrecognized selector sent to instance 0x103730870'