美文网首页
Runtime(二)消息发送和消息转发机制

Runtime(二)消息发送和消息转发机制

作者: 炒河粉儿 | 来源:发表于2019-08-16 10:02 被阅读0次

    消息发送

    [person read:book];
    
    objc_msgSend(person, @selector(read:), book);
    

    objc_msgSend的具体流程如下:

    1. 通过isa指针找到所属类
    2. 查找类的cache列表, 如果没有则下一步
    3. 查找类的”方法列表”
    4. 如果能找到与选择子名称相符的方法, 就跳至其实现代码
    5. 找不到, 就沿着继承体系继续向上查找
    6. 如果能找到与选择子名称相符的方法, 就跳至其实现代码
    7. 找不到, 执行”消息转发”.

    原则就是从当前类开始查找方法的实现,找不到就去父类找,父类找不到就去父类的父类找,一直找到根类,如果最终还是没有找到,则执行消息转发。

    消息转发

    消息转发的整个流程为三步。

    1. 动态方法解析。
    //实例方法
    + (BOOL)resolveInstanceMethod:(SEL)selector;
    //类方法
    + (BOOL)resolveClassMethod:(SEL)selector;
    
    1. 找备用接收者
    - (id)forwardingTargetForSelector:(SEL)selector;
    
    1. 消息签名和消息转发。
    //消息签名
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
    //消息转发
    - (void)forwardInvocation:(NSInvocation *)invocation;
    

    如果以上都无法处理消息的话,则会抛出异常。可以通过对下面方法的处理避免崩溃。

    - (void)doesNotRecognizeSelector:(SEL)aSelector;
    
    20160906224709111.png

    代码实现例子

    在Person类中定一个一个方法- (void)sendMessage:(NSString *)message;,但并没有去实现这个方法。

    在Dog类中定义并实现了了- (void)sendMessage:(NSString *)message;

    通过Person类的对象调用sendMessage:方法,来模拟实现消息的转发机制流程。

    外部调用

    Person *p = [[Person alloc]init];
    [p sendMessage:@"哈哈"];
    

    Person头文件

    #import <Foundation/Foundation.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface Person : NSObject
    
    - (void)sendMessage:(NSString *)message;
    
    @end
    
    NS_ASSUME_NONNULL_END
    

    Dog文件

    #import <Foundation/Foundation.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface Dog : NSObject
    
    - (void)sendMessage:(NSString *)message;
    
    @end
    
    NS_ASSUME_NONNULL_END
    
    
    
    #import "Dog.h"
    
    @implementation Dog
    
    - (void)sendMessage:(NSString *)message
    {
        NSLog(@"备用接收者实现:%@",message);
    }
    @end
    

    Person.m文件中的实现。

    当Person调用sendMessage方法时,因为person并没有实现这个方法,因此按照继承树层级查找查找不到该方法的实现。则会走第一个动态解析的步骤,在这一步,我们可以选择是否动态的去添加一下这个方法的实现,从而结束消息转发的流程,如果不解决这个问题,则会继续走下一步。

    //1.动态方法解析
    //是否要添加这个方法的实现,添加后这个方法就会实现,如果不添加则走第二步,越往后处理这个事件,消耗也是越来越大
    + (BOOL)resolveInstanceMethod:(SEL)sel
    {
        NSString *methodName = NSStringFromSelector(sel);
        if ([methodName isEqualToString:@"sendMessage:"]) {
            return class_addMethod([self class],sel, (IMP)sendMessage, "v@:@");
        }
    
        return NO;
    }
    
    void sendMessage(id self, SEL cmd, NSString *message){
        NSLog(@"%@",message);
    }
    

    当不在动态添加方法的情况下,消息转发继续想下一步执行,会去寻找备用者,也就是返回另一个类的对象,用这个对象去解决这个方法的实现问题。

    //2.找备用的接收者
    //转发到备用的接受者,让备用的接受者处理。
    - (id)forwardingTargetForSelector:(SEL)aSelector
    {
        NSString *methodName = NSStringFromSelector(aSelector);
        if ([methodName isEqualToString:@"sendMessage:"]) {
            return [Dog new];
        }
        //如果没有找到备用者,就走继承树
        return [super forwardingTargetForSelector:aSelector];
    }
    

    当找不到备用接收者的时候,则会进入到第三步,第三步分为两个小步,第一步先进行方法签名,第二步进行消息转发,将消息转发给其他对象。

    //3. 1.方法签名
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
    {
        NSString *methodName = NSStringFromSelector(aSelector);
        if ([methodName isEqualToString:@"sendMessage:"]) {
            return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
        }
        
        return [super methodSignatureForSelector:aSelector];
    }
    
    //3. 2.消息转发
    - (void)forwardInvocation:(NSInvocation *)anInvocation
    {
        SEL sel = [anInvocation selector];
        Dog *dog = [Dog new];
        //如果dog实现了这个方法,则把这个消息转发给dog,如果没实现,则走继承树
        if ([dog respondsToSelector:sel]) {
            [anInvocation invokeWithTarget:dog];
        }else {
            [super forwardInvocation:anInvocation];
        }
        
        [super forwardInvocation:anInvocation];
    }
    

    如果以上都无法处理这个消息,则消息无法处理,会产生崩溃,此时我们重写下面方法,则会解决崩溃问题。

    // 消息无法处理的情况 处理过后不会崩溃
    - (void)doesNotRecognizeSelector:(SEL)aSelector
    {
        NSLog(@"消息无法处理");
        
    }
    

    相关文章

      网友评论

          本文标题:Runtime(二)消息发送和消息转发机制

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