Runtime之msgSend探究

OC中的方法调用,其实都是转换为objc_megSend函数调用。

objc_megSend的执行流程可以分为3大阶段

  • 消息发送,如果消息发送成功,则调用相关方法。如果失败进入下一阶段。
  • 动态方法解析,如果成功,则调用相关方法。如果失败进入下一阶段。
  • 消息转发,如果失败则会"报找不到方法错误"

消息发送流程

消息发送.png

动态方法解析

流程

动态方法解析.png

动态添加方法

当消息发送进入动态方法解析阶段,调用+resolveInstanceMethod:+resolveClassMethod:方法时,可以在此时动态的添加方法。

有两种动态添加方法的方式。

  1. 动态其他OC方法

    @interface GLPerson : NSObject
    - (void) abc;
    @end
      
    #import 
    @implementation GLPerson
    
    - (void) other {
        NSLog(@"%@",NSStringFromSelector(_cmd));
        NSLog(@"%s",__func__);
    }
    
    + (BOOL)resolveInstanceMethod:(SEL)sel {
        if (sel == @selector(abc)) {
             //将other方法的实现添加。Method可以理解为等价于struct method_t *
            Method method = class_getInstanceMethod(self, @selector(other));
            class_addMethod(self,
                            sel,
                            method_getImplementation(method),
                            method_getTypeEncoding(method));
            return  YES;
        }
        return [super resolveInstanceMethod:sel];
    }
    @end
      
    //// ===== main
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            GLPerson *person = [[GLPerson alloc] init];
            [person abc];
          
        }
        return 0;
    }
    //程序输出:
    //abc
    /-[GLPerson other]
    
  2. 动态添加C语言函数

    @interface GLPerson : NSObject
    - (void) abc;
    @end
      
    #import 
    @implementation GLPerson
    
    - (void) other {
        NSLog(@"%@",NSStringFromSelector(_cmd));
        NSLog(@"%s",__func__);
    }
    
    + (BOOL)resolveInstanceMethod:(SEL)sel {
        if (sel == @selector(abc)) {
             class_addMethod(self,
                            sel,
                            /* C语言的函数名就是函数地址 */
                            (IMP)other,
                            "v16@0:8");
            return  YES;
        }
        return [super resolveInstanceMethod:sel];
    }
    @end
      
    //// ===== main
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            GLPerson *person = [[GLPerson alloc] init];
            [person abc];
          
        }
        return 0;
    }
    //程序输出:
    //abc
    //self=
    

消息转发流程

消息转发,顾名思义就是将消息转发给别的对象。

消息转发.png

当方法调用来到消息转发阶段:

  • 会先调用-forwardingTargetForSelector:或者+forwardingTargetForSelector:方法。

    • 如果返回值不为nil,则objc_msgSend(返回值,sel)

    • 如果返回值为nil或者没有实现,则进入第二个步骤

    • 实例对象调用-forwardingTargetForSelector:,类对象调用+forwardingTargetForSelector:

      forwardingTargetForSelector_demo.png

  • 如果返回值为nil,则会调用-methodSignatureForSelector:或者+methodSignatureForSelector:,该方法要求返回一个方法签名。

    • 如果返回nil,则会调用doesNotRecognizeSelector:抛出异常错误。

    • 如果返回值不为nil,则进入下一步骤

      methodSignatureForSelector_demo.png

  • methodSignatureForSelector:返回值不为nil,则调用forwardInvocation:,这个方法中可以进行任意操作。并且也不会导致崩溃。

    forwardingTargetForSelector_demo.png

你可能感兴趣的:(Runtime之msgSend探究)