美文网首页
Object-C 方法调用的本质

Object-C 方法调用的本质

作者: 石显军 | 来源:发表于2019-04-27 19:39 被阅读0次

    结论

    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 <objc/runtime.h> 再将下面的代码复制到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 方法调用的本质

          本文链接:https://www.haomeiwen.com/subject/wsflnqtx.html