OC 消息转发机制

作者: 心至靜行至遠 | 来源:发表于2016-08-19 10:36 被阅读776次

首先我们看一下objc_msgSend它具体是如何发送消息:

  1. 首先根据receiver对象的isa指针获取它对应的class
  2. 优先在class的cache查找message方法,如果找不到,再到
    methodLists查找
  3. 如果没有在class找到,再到super_class查找
  4. 一旦找到message这个方法,再依据receiver 中的self 指针找到当前的对象,调用当前对象的具体实现的方法(IMP),然后传递参数,调用实现方法。


    图1

    当对象收到无法解读的消息后,就会启动“消息转发”(message forwarding)机制,程序员可经由此过程告诉对象应该如何处理未知消息。

消息转发全流程图:

图2

方案一:

+ (BOOL)resolveInstanceMethod:(SEL)sel

+ (BOOL)resolveClassMethod:(SEL)sel

首先,系统会调用resolveInstanceMethod(当然,如果这个方法是一个类方法,就会调用resolveClassMethod)让你自己为这个方法增加实现。

demo1:

定义一个Person,创建了一个Person类的对象p,然后调用p的run方法,注意,这个run方法是没有写实现的。

Person *p = [Person alloc] init];
[p run];

进入Person类的.m文件,我实现了resolveInstanceMethod这个方法为我的Person类动态增加了一个run方法的实现。

void run (id self, SEL _cmd) {

    NSLog(@"跑");
}

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    
    if(sel == @selector(run)){
        class_addMethod(self, sel, (IMP)run, "v@:");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

方案二:

- (id)forwardingTargetForSelector:(SEL)aSelector

第二套方法,forwardingTargetForSelector,这个方法返回你需要转发消息的对象。

demo2:

为了便于演示消息转发,我们新建了一个汽车类Car,并且实现了Car的run方法。

现在我不去对方案一的resolveInstanceMethod做任何处理,直接调用父类方法。可以看到,系统已经来到了forwardingTargetForSelector方法,我们现在返回一个Car类的实例对象。

- (id)forwardingTargetForSelector:(SEL)aSelector {
    return [[Car alloc] init];  
}

继续运行,程序就来到了Car类的run方法,这样,我们就实现了消息转发。

方案三:

- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector;

- (void)forwardInvocation:(NSInvocation *)invocation;

methodSignatureForSelector用来生成方法签名,这个签名就是给forwardInvocation中的参数NSInvocation调用的。

开头我们要找的错误unrecognized selector sent to instance原因,原来就是因为methodSignatureForSelector这个方法中,由于没有找到run对应的实现方法,所以返回了一个空的方法签名,最终导致程序报错崩溃。

所以我们需要做的是自己新建方法签名,再在forwardInvocation中用你要转发的那个对象调用这个对应的签名,这样也实现了消息转发。

- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {

    NSString *sel = NSStringFromSelector(selector);
    if([sel isEqualString:@"run"]) {
        return [NSMethodSignature signatureWithObjcTypes:"v@:"];
    }
    return [sunper methodSignatureForSelector:selector];
}


- (void)forwardInvocation:(NSInvocation *)invocation {

    SEL selector = [invocation selector];
    //新建需要转发消息的对象
    Car *car = [[Car alloc] init];
    if([car respondsToSelector:selector]){
        [invocation invokeWithTarget:car];
    }
}

关于生成签名的类型"v@:"解释一下。每一个方法会默认隐藏两个参数,self、_cmd,self代表方法调用者,_cmd代表这个方法的SEL,签名类型就是用来描述这个方法的返回值、参数的,v代表返回值为void,@表示self,:表示_cmd。

注意一点,前一套方案实现后一套方法就不会执行。如果这几套方案你都没有做处理,那么程序就会报错crash。

相关文章

  • 关于Runtime 消息发送机制的延伸

    说到OC 不得不说一下OC 的消息转发机制;何为OC 的消息转发机制;其实就是这样的; Objc 在向一个对象发送...

  • OC消息机制,消息转发机制

    Runtime简称运行时,其中最主要的是消息机制 概述 C 与 OC 的不同 1.C 语言,函数的调用在编译的时候...

  • Runtime知识点整理1

    OC消息机制?消息转发机制流程?什么是Runtime?什么场景下使用? ==============巴拉巴拉......

  • oc消息转发机制

    一、消息转发机制 在OC中,调用一个对象的方法,实际上是给对象发了一条消息,在编译Objective-C函数调用的...

  • OC消息转发机制

    当一个对象收到它没实现的消息的时候,通常会发生如下的情况。 调用+(BOOL)resolveInstanceMet...

  • OC 消息转发机制

    首先了解几个概念:class 的定义 method的定义 消息转发本质:在运行时将方法地址(imp)和一个名字(s...

  • OC 消息转发机制。

    当调用一个 NSObject 对象不存在的方法时,并不会马上抛出异常,而是会经过多层转发,层层调用对象的-reso...

  • OC 消息转发机制

    首先我们看一下objc_msgSend它具体是如何发送消息: 首先根据receiver对象的isa指针获取它对应的...

  • OC消息转发机制

    消息转发的两大阶段 先征询接收者,所属的类,看其是否能够动态添加方法,以处理这个『未知的选择子』,这叫做动态方法解...

  • OC消息转发机制

    暂时先看这篇文章消息转发

网友评论

    本文标题:OC 消息转发机制

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