欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2022-06-26 13:04:15
先上图: 下面根据具体代码看这张图。 一、创建一个Person类, Person.h Person.m 大家可以看到,Person类只声明了 sendMessage:方法,在.m文件里没有实现这个方法。 这时,如果在viewController中调用Person类的sendMessage方法,程序会 ......

先上图:

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查询地址:)

 

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);
}