• Runtime消息动态解析与转发流程


    先上图:

    下面根据具体代码看这张图。

    一、创建一个Person类,

     Person.h

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

    Person.m

    #import "Person.h"
    #import <objc/runtime.h>
    
    @implementation Person
    
    @end

    大家可以看到,Person类只声明了 sendMessage:方法,在.m文件里没有实现这个方法。

    这时,如果在viewController中调用Person类的sendMessage方法,程序会发生崩溃。

    #import "ViewController.h"
    #import "Person.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        [[[Person alloc]init] sendMessage:@"Hello"];
        
        
    }

    结合上面的图片,我们说说消息处理的机制。

    1.当我们调用的方法没有具体的实现时,会调用

    + (BOOL)resolveInstanceMethod:(SEL)sel;

    +(BOOL)resolveInstanceMethod:(SEL)sel{
        
        NSString *methodName = NSStringFromSelector(sel);
        if ([methodName isEqualToString:@"sendMessage:"]) {
            //我们可以在这里添加方法的实现
            return class_addMethod(self, sel, (IMP)sendMessage, "v@:@");
        }
        
        return NO;
        
    }
    
    void sendMessage (id self, SEL _cmd, NSString *message){
        NSLog(@"message=%@",message);
    }

    BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types):为类动态添加方法。如果有同名会返回NO,成功返回YES。

    其中的参数types查询地址:(v:表示void, @:表示类型,等等

    2. 如果 resolveInstanceMethod:方法返回NO,调用

    -(id)forwardingTargetForSelector:(SEL)aSelector;

    这个方法是找备用者,比如:Animal类。

    Animal.h

    #import <Foundation/Foundation.h>
    
    @interface Animal : NSObject
    
    @end

    Animal.m

    #import "Animal.h"
    
    @implementation Animal
    
    -(void)sendMessage:(NSString *)message{
        NSLog(@"message=%@",message);
    }
    
    @end

    Animal类没有声明sendMessage:方法,但在.m文件里有这个方法的实现,可以作为备用者。如下:

    -(id)forwardingTargetForSelector:(SEL)aSelector{
        
        NSString *methodName = NSStringFromSelector(aSelector);
        if ([methodName isEqualToString:@"sendMessage:"]) {
            if ([[Animal new] respondsToSelector:aSelector]) {
                return [Animal new];
            }
        }
        
        
        return [super forwardingTargetForSelector:aSelector];
    }

    3. 如果 forwardingTargetForSelector:(SEL)aSelector返回 nil。

    // 若前两种方法都不处理,则走这里
    //    1)方法签名
    -(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
        
        NSString *methodName = NSStringFromSelector(aSelector);
        if ([methodName isEqualToString:@"sendMessage:"]) {
            return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
            }
        return [super methodSignatureForSelector:aSelector];
    }
    //  2) 签名后,消息转发,找备用者
    -(void)forwardInvocation:(NSInvocation *)anInvocation{
        
        SEL selector = [anInvocation selector];
        Animal *animal = [Animal new];
        if ([animal respondsToSelector:selector]) {
            [anInvocation invokeWithTarget:animal];
        } else{
            [super forwardInvocation:anInvocation];
        }
    }

    4.如果走到第3步,仍然不做处理,如下:

    -(void)forwardInvocation:(NSInvocation *)anInvocation{
       
        [super forwardInvocation:anInvocation];
    }

    这时为了程序的健壮性,防止崩溃,可以用以下方法处理。

    //  若前3方法都不处理,为了防止崩溃,可调用此方法
    -(void)doesNotRecognizeSelector:(SEL)aSelector{
        NSString *methodName = NSStringFromSelector(aSelector);
        NSLog(@"找不到 %@ 这个方法的实现",methodName);
    }

    附加源码

  • 相关阅读:
    OC基础数据类型-NSData-NSMutableData-NSString
    python学习:格式化输出
    python学习:修改字符串大小写
    python学习:输出九九乘法表
    python学习:输入中文
    python学习:缩进
    python学习:注释、获取用户输入、字符串拼接、运算符、表达式
    python学习:条件语句if、else
    python学习:常量和变量
    hdoj1584 蜘蛛牌 (区间型动态规划)
  • 原文地址:https://www.cnblogs.com/lfyDragon/p/9177710.html
Copyright © 2020-2023  润新知