美文网首页
iOS objc_msgSend笔记

iOS objc_msgSend笔记

作者: 山杨 | 来源:发表于2021-11-07 02:25 被阅读0次

    三大阶段:1. 消息发送 2. 动态方法解析 3. 消息转发

    消息发送
    • 从receiverClass的class_rw_t中查找方法
      • 已经排好序的方法列表,使用二分查找
      • 没有排序的方法列表,使用遍历查找
    • receiver通过isa找到receiverClass
    • receiverClass通过superClass指针找到superClass
    动态方法解析
    void c_errorMethod(id self, SEL _cmd)
    {
       NSLog(@"%s", __func__);
    }
    - (void)errorMethod {
       NSLog(@"%s", __func__);
    }
    
    // 动态方法解析 - 实例对象方法
    + (BOOL)resolveInstanceMethod:(SEL)sel {
    
       if (sel == @selector(method)) {
           
           Method errorMethod = class_getInstanceMethod(self, @selector(errorMethod));
           IMP errorImp = method_getImplementation(errorMethod);
           const char *type = method_getTypeEncoding(errorMethod);
           class_addMethod(self, sel, errorImp, type);
           return YES;
       }
       return [super resolveInstanceMethod:sel];
    }
    
    // 动态方法解析 - 类方法
    + (BOOL)resolveClassMethod:(SEL)sel {
    
       if (sel == @selector(method)) {
           // 获取元类对象
           Class metaClass = object_getClass(self);
           // c_errorMethod的编码为"v@:"
           // 规则:v表示返回值void,@表示第1个参数是对象类型,:表示第2个参数是SEL类型
           // 完整规则参考TypeEncoding官方文档
           const char *type = "v@:";
           class_addMethod(metaClass, sel, (IMP)c_errorMethod, type);
           return YES;
       }
       return [super resolveClassMethod:sel];
    }
    

    TypeEncoding官方文档

    如果已经进行过动态方法解析,依然没有处理该消息,则会进入消息转发阶段

    消息转发
    - (id)forwardingTargetForSelector:(SEL)aSelector {
        
        if (aSelector == @selector(method)) {
            // 如果返回了一个新的对象,则会触发objc_msgSend(aNewObj, aSelector)
            return aNewObj;
        }
        return [super forwardingTargetForSelector:aSelector];
    }
    // 如果forwardingTargetForSelector:没有返回一个合适的对象
    // 那么就会触发methodSignatureForSelector:方法
    // 返回一个NSInvocation对象给forwardInvocation:.
    // 通过子类重写该方法,将消息转发给其他对象
    // NSInvocation对象使用来自methodSignatureForSelector:返回的信息创建
    /*
    anInvocation.target;//消息接收对象
    anInvocation.selector;//方法名
    [anInvocation getReturnValue:];//返回值
    [anInvocation getArgument: atIndex:];// 参数
    */
    - (void)forwardInvocation:(NSInvocation *)anInvocation {
        
    }
    // 如果返回值为nil,则不会触发forwardInvocation:
    // 而会去直接触发doesNotRecognizeSelector:抛出异常
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
        
        if (aSelector == @selector(method)) {
    
            return [NSMethodSignature signatureWithObjCTypes:"v@:"];
        }
        return [super methodSignatureForSelector:aSelector];
    }
    
    //备注: doesNotRecognizeSelector:方法可以重写,但不能正常返回
    //(换句话说就是在结束时必须引发NSInvalidArgumentException异常)
    
    

    值得注意的是系统提供的-forwardingTargetForSelector:方法是针对某个实例对象调用对象方法的,如果要调用类对象的类方法需要用
    +forwardingTargetForSelector:方法。
    同样forwardInvocationmethodSignatureForSelector也遵循这样的方式修改为针对类对象方法的调用。

    相关文章

      网友评论

          本文标题:iOS objc_msgSend笔记

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