iOS学习专题底层开发-基础知识

runtime 消息转发机制(最详细流程)

2019-05-05  本文已影响29人  书上说says

说到runtime,就不得不讲到最重要的消息转发机制,转发的流程图如下:


173709-7bcc4302c515f1e0.png

在OC里面,所有的方法调用,在运行时的时候都可以看作是objc_msgSend(obj,@selector())的调用

1.正常的方法调用

person.h文件,声明方法

#import <Foundation/Foundation.h>

@interface Person : NSObject
-(void)sendMessage:(NSString *)msg;
@end

person.m文件

#import "Person.h"
#import "Child.h"
#import <objc/runtime.h>
@implementation Person
- (void)sendMessage:(NSString *)msg{
    NSLog(@"person send msg:%@",msg);
}
@end

如果person.m实现了-(void)sendMessage:(NSString *)msg 方法,那么将会调用该方法,结果如图:


image.png

如果没有实现这个方法,则会报错没有该方法


image.png

那么实现消息转发机制的方法,就可以解决这个问题:

  1. 如果没有实现改方法,会先进行动态解析,调用+ (BOOL)resolveInstanceMethod:(SEL)sel方法
#import "Person.h"
#import "Child.h"
#import <objc/runtime.h>
@implementation Person

//- (void)sendMessage:(NSString *)msg{
//    NSLog(@"person send msg:%@",msg);
//}


void sendMessage(id self,SEL _cmd,NSString *msg) {
    NSLog(@"send msg:%@",msg);
}

//动态解析
+ (BOOL)resolveInstanceMethod:(SEL)sel{
    NSLog(@"动态方法解析");
    NSString *methodName = NSStringFromSelector(sel);
    if ([methodName isEqualToString:@"sendMessage:"]) {
        return class_addMethod(self, sel, (IMP)sendMessage, "v@:@");
    }
    return NO;
}
@end

image.png

2.如果第一步的动态解析也没有找到方法,那么将会进行快速转发,调用- (id)forwardingTargetForSelector:(SEL)aSelector可以在本类方法列表未找到方法的时候,转发给其他类,并返回该类对象:

Child.m 文件实现了一个方法

#import "Child.h"

@implementation Child
- (void)sendMessage:(NSString *)msg{
    NSLog(@"child send msg:%@",msg);
}
@end
#import "Person.h"
#import "Child.h"
#import <objc/runtime.h>
@implementation Person

//- (void)sendMessage:(NSString *)msg{
//    NSLog(@"person send msg:%@",msg);
//}


//void sendMessage(id self,SEL _cmd,NSString *msg) {
//    NSLog(@"send msg:%@",msg);
//}

//动态解析
+ (BOOL)resolveInstanceMethod:(SEL)sel{
//    NSLog(@"动态方法解析");
//    NSString *methodName = NSStringFromSelector(sel);
//    if ([methodName isEqualToString:@"sendMessage:"]) {
//        return class_addMethod(self, sel, (IMP)sendMessage, "v@:@");
//    }
    return NO;
}

//快速转发
- (id)forwardingTargetForSelector:(SEL)aSelector{
    NSString *methodName = NSStringFromSelector(aSelector);
    if ([methodName isEqualToString:@"sendMessage:"]) {
        return [Child new];
    }
    return [super forwardingTargetForSelector:aSelector];
}
@end

运行结果:


image.png

3.如果没有快速转发也失败了,那么将会返回nil,进行慢速转发,慢速转发需要实现两个方法:
(1)- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
(2) - (void)forwardInvocation:(NSInvocation *)anInvocation

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

//- (void)sendMessage:(NSString *)msg{
//    NSLog(@"person send msg:%@",msg);
//}


//void sendMessage(id self,SEL _cmd,NSString *msg) {
//    NSLog(@"send msg:%@",msg);
//}

//动态解析
+ (BOOL)resolveInstanceMethod:(SEL)sel{
//    NSLog(@"动态方法解析");
//    NSString *methodName = NSStringFromSelector(sel);
//    if ([methodName isEqualToString:@"sendMessage:"]) {
//        return class_addMethod(self, sel, (IMP)sendMessage, "v@:@");
//    }
    return NO;
}

//快速转发
- (id)forwardingTargetForSelector:(SEL)aSelector{
//    NSString *methodName = NSStringFromSelector(aSelector);
//    if ([methodName isEqualToString:@"sendMessage:"]) {
//        return [Child new];
//    }
    return [super forwardingTargetForSelector:aSelector];

}

//慢速转发 1.签名 2.转发
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    NSString *methodName = NSStringFromSelector(aSelector);
    if ([methodName isEqualToString:@"sendMessage:"]) {
        return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
    }
    return [super methodSignatureForSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation{
    SEL sel = anInvocation.selector;
    Child *child = [Child new];
    if ([child respondsToSelector:sel]) {
        [anInvocation invokeWithTarget:child];
    }else {
        [super forwardInvocation:anInvocation];
    }

}

结果如下,还是转发给了child的类对象,消息转发成功:


image.png

4.最后,如果消息转发失败,那么将会报错,但是为了程序防止崩溃,还是有方法可以处理的,因为到最后如果还没有转发成功,那么将会走-(void)doesNotRecognizeSelector:(SEL)aSelector方法:

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

//- (void)sendMessage:(NSString *)msg{
//    NSLog(@"person send msg:%@",msg);
//}


//void sendMessage(id self,SEL _cmd,NSString *msg) {
//    NSLog(@"send msg:%@",msg);
//}

//动态解析
+ (BOOL)resolveInstanceMethod:(SEL)sel{
//    NSLog(@"动态方法解析");
//    NSString *methodName = NSStringFromSelector(sel);
//    if ([methodName isEqualToString:@"sendMessage:"]) {
//        return class_addMethod(self, sel, (IMP)sendMessage, "v@:@");
//    }
    return NO;
}

//快速转发
- (id)forwardingTargetForSelector:(SEL)aSelector{
//    NSString *methodName = NSStringFromSelector(aSelector);
//    if ([methodName isEqualToString:@"sendMessage:"]) {
//        return [Child new];
//    }
    return [super forwardingTargetForSelector:aSelector];

}

//慢速转发 1.签名 2.转发
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    NSString *methodName = NSStringFromSelector(aSelector);
    if ([methodName isEqualToString:@"sendMessage:"]) {
        return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
    }
    return [super methodSignatureForSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation{
//    SEL sel = anInvocation.selector;
//    Child *child = [Child new];
//    if ([child respondsToSelector:sel]) {
//        [anInvocation invokeWithTarget:child];
//    }else {
//        [super forwardInvocation:anInvocation];
//    }
    
    [super forwardInvocation:anInvocation];

}
-(void)doesNotRecognizeSelector:(SEL)aSelector{
    NSLog(@"无法识别该消息:%@",NSStringFromSelector(aSelector));
}

@end

运行结果:


image.png

到此一个完整的消息转发机制例子就演示完毕了,如果有需要的可以下载完整的源码,本文也是参考了部分大神的文章,对自己的一个小结,如果有误导的地方,欢迎大家指正以及补充***

上一篇 下一篇

猜你喜欢

热点阅读