iOS 消息转发机制

消息机制: 调用任何方法的时候 其实都相当于给这个对象发送一个消息

 

举例 如下方法

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self test];
    // Do any additional setup after loading the view.
}

- (void)test{
    NSLog(@"%@",self); //<ViewController: 0x104e089b0>
    NSLog(@"%@",NSStringFromSelector(_cmd)); //test
}

@end

每一个方法的调用其实默认都携带了两个参数 一个是 id 类型的self 一个是SEL 类型的_cmd 

_cmd 其实是方法编号

在发送这个消息的时候、系统会根据方法编号去方法缓存列表查询方法(汇编查询效率较高)

如果缓存中未找到开启慢速查找 按照实例对象-> 类对象 -> 元类对象的顺序查找 (实例方法存在去类对象中、类方法存在于元类对象中) 如果找到就缓存起来

如果没找到就会进入动态方法解析阶段 如下 Person 没有实现run 进入消息动态放解析阶段时、可以动态添加方法

@interface Person : NSObject
- (void)run;
@end


#import "Person.h"
#import <objc/message.h>
@implementation Person
/*
   动态解析 对象方法
 */
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(run)) {
        class_addMethod(self, sel, (IMP)dynamicMethod, "v@:");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

void dynamicMethod(){
    NSLog(@"%s",__func__);
}
@end

如果还没没有找到则进入消息转发流程

 

 

- (id)forwardingTargetForSelector:(SEL)aSelector{
    if (aSelector == @selector(run)) {
        return [[Cat alloc] init];
    }
    return [super forwardingTargetForSelector:aSelector];
}
/*
 如果没有实现forwardingTargetForSelector 就会调用下面的返回方法签名 如果返回了方法签名就会调用forwardInvocation  否则直接调用doesNotRecongnizeSelector
 */
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    if (aSelector == @selector(run)) {
        return [NSMethodSignature signatureWithObjCTypes:"v@:"];
    }
    return [super methodSignatureForSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation{
    [anInvocation invokeWithTarget:[[Cat alloc] init]];
    NSLog(@"anInvocation");
}

void dynamicMethod(){
    NSLog(@"%s",__func__);
}
@end

 实际应用 利用消息转发机制 可以避免开发中的一些 unrecognized selector sent to崩溃

@interface Cat : NSObject
- (void)run;
- (void)walk;
@end


#import "Cat.h"

@implementation Cat
- (void)run{
    NSLog(@"%s",__func__);
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    if ([self respondsToSelector:aSelector]) {
        return [super methodSignatureForSelector:aSelector];
    }
    return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
    NSLog(@"没有找到%@",NSStringFromSelector(anInvocation.selector));
}
@end

 

posted @ 2021-04-27 12:08  ZhangShengjie  阅读(187)  评论(0编辑  收藏  举报