Object-C 方法调用的本质

结论

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'

流程图

1197929-2de08be6100cd895.jpeg

你可能感兴趣的:(Object-C 方法调用的本质)