美文网首页
Runtime之消息发送和消息转发

Runtime之消息发送和消息转发

作者: Tonyliu_ | 来源:发表于2018-04-19 16:08 被阅读0次

    简介:

    [object doSomething];
    编译器会转换为下面形式:
    objc_msgSend(receiver, selector);
    

    如果消息含有参数,则为:

    objc_msgSend(receiver, selector, arg1, arg2, ...)
    

    1、消息发送机制

    • 检查selector 是否需要忽略(比如 Mac OS X 开发,有了垃圾回收就不理会 retain,release 这些函数。)
    • 检查target是否为nil,如果为nil,直接cleanup,然后return(这就是为什么可以向nil发送消息的原因)
    • 然后在target的Class中根据Selector去找IMP。

    寻找IMP的过程:

    • 先从当前class的cache方法列表(cache methodLists)里去找。
    • 如果找到了,跳到对应函数实现。
    • 如果没找到,就从class的方法列表(methodLists)里找。
    • 如果还找不到,就到super class的方法列表里找,直到找到基类(NSObject)为止。
    • 最后再找不到,就会进入动态方法解析和消息转发的机制。

    2、消息转发机制

    • 消息发送是Runtime通过selector快速查找IMP的过程,有了函数指针就可以执行对应的方法实现;
    • 消息转发是在查找IMP失败后执行一系列转发流程的慢速通道,如果不作转发处理,则会打日志和抛出异常。

    ①动态方法解析:

    对象在收到无法解读的消息之后,首先将调用所属类的下列类方法:

    +(BOOL)resolveInstanceMethod:(SEL)name
    

    demo解析:

    #import <Foundation/Foundation.h>@interface Person : NSObject
    - (void)eat; //没有实现
    @end
    
    运行结果 image-20180418170832412.png

    在person.m中写入以下代码,对象在接受的无法解读的消息后,首先会调用+(BOOL)resolveInstanceMethod:(SEL)sel或者+ (BOOL)resolveClassMethod:(SEL)sel, 询问是否有动态添加方法来进行处理,处理实例如下

    @implementation Person
    +(BOOL)resolveInstanceMethod:(SEL)sel
    {
        NSLog(@"sel = %@",NSStringFromSelector(sel));
        return [super resolveInstanceMethod:sel];
    }
    
    结果如下: image-20180418172155127.png

    具体添加方法如下:

    + (BOOL)resolveInstanceMethod:(SEL)sel {
        if (sel == @selector(eat))
        {
            class_addMethod([self class], sel, (IMP)addeat, "V@");
            return YES;
        }
        return [super resolveInstanceMethod:sel];
    }
    void addeat(id self,SEL sel,NSString *str) 
    {
        NSLog(@"添加成功");
    }
    
    class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp, const char * _Nullable types)
        参数一:被添加方法的累
        参数二:要添加的方法
        参数三:实现这个方法的函数
        参数四:定义返回值和参数类型的字符串(i:代表int  v代表void @:代表参数)
    

    如果动态添加失败,就会进入下面的操作

    ②消息转发重定向

    - (id)forwardingTargetForSelector:(SEL)aSelector
    

    在消息转发机制执行前,Runtime系统会再给我们一次偷梁换柱的机会,即通过重载- (id)forwardingTargetForSelector:(SEL)aSelector方法替换消息的接受者为其他对象:

    新建一个tempObject,定义eat方法,在.m文件中实现

    #import "tempObject.h"
    
    @implementation tempObject
    - (void)eat
    {
        NSLog(@"i am tempObject");
    }
    @end
    

    在person类中将接受消息对象换为tempObject

    -(id)forwardingTargetForSelector:(SEL)aSelector
    {
        return [[tempObject alloc]init];
    }
    

    结果如下:


    image-20180419150039116.png

    ③ 消息转发

    首先获取方法的签名,拿着签名再去配发消息,如果不能接受消息就会抛出异常

    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
    - (void)forwardInvocation:(NSInvocation *)anInvocation;
    

    获取方法签名

    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE("")
    {
        //转化字符
        NSString *sel = NSStringFromSelector(aSelector);
        //判断, 手动生成签名
        if([sel isEqualToString:@"eat"])
        {
            return [NSMethodSignature signatureWithObjCTypes:"v@:"];
        }
        else
        {
            return [super methodSignatureForSelector:aSelector];
        }
    }
    

    拿到方法签名配发消息

    - (void)forwardInvocation:(NSInvocation *)anInvocation OBJC_SWIFT_UNAVAILABLE(""){
        NSLog(@"forwardInvocation: %@", NSStringFromSelector([anInvocation selector]));
        //取到消息
        SEL seletor = [anInvocation selector];
        //转发
        tempObject *temp = [[tempObject alloc]init];
        if([temp respondsToSelector:seletor])
        {
            //调用对象,进行转发
            [anInvocation invokeWithTarget:temp];
        }
        else
        {
            return [super forwardInvocation:anInvocation];
        }
    }
    

    如果不能接受消息,抛出异常

    - (void)doesNotRecognizeSelector:(SEL)aSelector
    {
        NSString *selStr = NSStringFromSelector(aSelector);
        NSLog(@"%@不存在",selStr);
    }
    

    参考文章:

    runtime 消息转发:https://www.jianshu.com/p/8cd06cd496d5
    ​ runtime消息转发demo:http://www.2bjs.com/%E6%9E%B6%E6%9E%84/iOS%20%E6%B6%88%E6%81%AF%E8%BD%AC%E5%8F%91%E6%9C%BA%E5%88%B6Demo%E8%A7%A3%E6%9E%90/

    相关文章

      网友评论

          本文标题:Runtime之消息发送和消息转发

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