美文网首页
Runtime - objc_msgSend

Runtime - objc_msgSend

作者: 大白菜s | 来源:发表于2019-08-14 14:52 被阅读0次
    [person test];
    ((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("test"));
    
    消息发送阶段:负责从类及父类的缓存列表及方法列表查找方法。
    消息发送流程
    动态解析阶段:如果消息发送阶段没有找到方法,则会进入动态解析阶段,负责动态的添加方法实现。

    为类动态添加方法:

    #import "MJPerson.h"
    #import <objc/runtime.h>
    
    @implementation MJPerson
    - (void)other
    {
        NSLog(@"%s", __func__);
    }
    + (BOOL)resolveInstanceMethod:(SEL)sel
    {
        if (sel == @selector(test)) {
            // 获取其他方法
            Method method = class_getInstanceMethod(self, @selector(other));
            // 动态添加test方法的实现
            class_addMethod(self, sel,
                            method_getImplementation(method),
                            method_getTypeEncoding(method));
            // 返回YES代表有动态添加方法
            return YES;
        }
        return [super resolveInstanceMethod:sel];
    }
    
    动态解析阶段
    消息转发阶段:如果也没有实现动态解析方法,则会进行消息转发阶段,将消息转发给可以处理消息的接受者来处理。

    当本类没有实现方法,并且没有动态解析方法,就会调用forwardingTargetForSelector函数,进行消息转发,我们可以实现forwardingTargetForSelector函数,在其内部将消息转发给可以实现此方法的对象。
    如果forwardingTargetForSelector函数返回为nil或者没有实现的话,就会调用methodSignatureForSelector方法,用来返回一个方法签名,这也是我们正确跳转方法的最后机会。
    如果methodSignatureForSelector方法返回正确的方法签名就会调用forwardInvocation方法,forwardInvocation方法内提供一个NSInvocation类型的参数,NSInvocation封装了一个方法的调用,包括方法的调用者,方法名,以及方法的参数。在forwardInvocation函数内修改方法调用对象即可。
    如果methodSignatureForSelector返回的为nil,就会来到doseNotRecognizeSelector:方法内部,程序crash提示无法识别选择器unrecognized selector sent to instance。


    消息转发
    - (id)forwardingTargetForSelector:(SEL)aSelector
    {
        // 返回能够处理消息的对象
        if (aSelector == @selector(driving)) {
            // 返回nil则会调用methodSignatureForSelector方法
            return nil; 
            // return [[Car alloc] init];
        }
        return [super forwardingTargetForSelector:aSelector];
    }
    
    // 方法签名:返回值类型、参数类型
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
    {
        if (aSelector == @selector(driving)) {
           // return [NSMethodSignature signatureWithObjCTypes: "v@:"];
           // return [NSMethodSignature signatureWithObjCTypes: "v16@0:8"];
           // 也可以通过调用Car的methodSignatureForSelector方法得到方法签名,这种方式需要car对象有aSelector方法
            return [[[Car alloc] init] methodSignatureForSelector: aSelector];
    
        }
        return [super methodSignatureForSelector:aSelector];
    }
    
    //NSInvocation 封装了一个方法调用,包括:方法调用者,方法,方法的参数
    //    anInvocation.target 方法调用者
    //    anInvocation.selector 方法名
    //    [anInvocation getArgument: NULL atIndex: 0]; 获得参数
    - (void)forwardInvocation:(NSInvocation *)anInvocation
    {
    //   anInvocation中封装了methodSignatureForSelector函数中返回的方法。
    //   此时anInvocation.target 还是person对象,我们需要修改target为可以执行方法的方法调用者。
    //   anInvocation.target = [[Car alloc] init];
    //   [anInvocation invoke];
        [anInvocation invokeWithTarget: [[Car alloc] init]];
    }
    
    // 打印内容
    // 消息转发[5781:2164454] car driving
    

    相关文章

      网友评论

          本文标题:Runtime - objc_msgSend

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