美文网首页
OC底层原理12 - 消息转发

OC底层原理12 - 消息转发

作者: 卡布奇诺_95d2 | 来源:发表于2021-02-01 16:36 被阅读0次

消息转发

经过了快速查找,慢速查找,动态方法解析之后,仍然没有找到SEL对应的IMP,此时我们在抛出异常信息的信息的时候,查看一下当前堆栈情况

frame #9: 0x00000001002e8c7e libobjc.A.dylib`objc_exception_throw(obj="-[HQPerson say666]: unrecognized selector sent to instance 0x100665a40") at objc-exception.mm:591:5
frame #10: 0x00007fff314de936 CoreFoundation`-[NSObject(NSObject) doesNotRecognizeSelector:] + 132
frame #11: 0x00007fff313c3ec0 CoreFoundation`___forwarding___ + 1427
frame #12: 0x00007fff313c3898 CoreFoundation`__forwarding_prep_0___ + 120
frame #13: 0x0000000100003b1e HQObjc`main(argc=1, argv=0x00007ffeefbff4f0) at main.m:154:9 [opt]
frame #14: 0x00007fff6b547cc9 libdyld.dylib`start + 1

从上面的堆栈信息中可以看出,在doesNotRecognizeSelector函数(该函数为抛出未找到selector的异常信息)之前,有两步CoreFoundation的操作,但___forwarding_____forwarding_prep_0___.CoreFoundation是不开源的,那如何分析这两个操作呢?这可以通过使用hopper工具进行反编译查看这两个函数的流程.本章忽略如何使用hopper工具.直接给出结果:

  • __forwarding_prep_0___函数中调用___forwarding___;
  • ___forwarding___的伪代码中,首先是查看当前类是否实现forwardingTargetForSelector方法(快速消息转发).
    • 若实现该方法,进执行该方法.
    • 若未实现该方法,则查看是否实现methodSignatureForSelector方法(慢速消息转发).
      • methodSignatureForSelector方法未实现,则直接报错.
      • methodSignatureForSelector方法有实现,则判断该方法的返回值.
        • 返回值为nil,直接报错.
        • 返回值不为nil,在forwardInvocation方法中对invocation进行处理.

快速消息转发

forwardingTargetForSelector:当进行快速查找,慢速查找,动态方法解析之后,仍然没有查到SEL对应的IMP,这个时候,苹果给我们提供了第二次挽救的机会,即快速消息转发.

快速消息转发的中心思想是,既然在当前消息接收者中找不到SEL,那通过forwardingTargetForSelector函数,将当前的消息接收者可以转发给其它能处理该消息的类.

注意:forwardingTargetForSelector方法分为实例方法类方法,这两个方法在NSObject中都有实现,只是返回值为nil.

接下来尝试在第二次挽救机会中对HQPerosn的实例方法say666进行挽救.

  • 由于快速消息转发是将消息转发给能处理该消息的其它类,因此需要定义一个其它类,并实现say666方法.
@interface HQHuman : NSObject
-(void)say666;
@end
@implementation HQHuman
-(void)say666{
    NSLog(@"%s", __func__);
}
@end
  • 在HQPerson类中,由于是对实例方法的挽救,因此需要实现forwardingTargetForSelector实例方法.
- (id)forwardingTargetForSelector:(SEL)aSelector{
    if(aSelector == NSSelectorFromString(@"say666")){
        return [HQHuman alloc];
    }
    return nil;
}
  • 尝试向HQPerson对象发送say666消息,查看结果
2021-02-01 15:33:18.311088+0800 HQObjc[37844:2499110] Hello, World!
2021-02-01 15:33:21.403814+0800 HQObjc[37844:2499110] -[HQHuman say666]
2021-02-01 15:33:22.842117+0800 HQObjc[37844:2499110] end~
Program ended with exit code: 0

由结果可以看到,HQPerson类虽然没有实现say666的实例方法,但经过快速消息转发后,将say666消息接收者修改成HQHuman了.最终由HQHuman完成消息的执行.

慢速消息转发

要了解慢速消息转发,需要先了解NSInvocation.

NSInvocation命令模式的一种传统实现,它把一个目标、一个选择器、一个方法签名和所有的参数都塞进一个对象里,这个对象可以先存储起来,以备将来调用.

NSInvocation还包含一个方法签名(NSMethodSignature),它封装了一个方法的返回类型参数类型,记住它不包括方法名称,只有返回类型和参数类型。可以通过以下方法,手动的创建一个方法签名:

NSMethodSignature *sig = [NSMethodSignature signatureWithObjCTypes:"v@:"];

最后,NSInvocation还包含了所有的参数.

回到慢速消息转发.

当经过第二次挽救时,仍然未找到该SEL对应的IMP,此时苹果最后给了一次机会.即先调用methodSignatureForSelector,查看当前是否有返回方法签名.
若当前未返回方法签名,即返回nil,则报错.
若返回的方法签名不为nil,则创建一个NSInvocation,并传递给forwardInvocation.在前面对NSInvocation的描述中知道,有了NSInvocation对象,就能完成消息的执行.

注意:methodSignatureForSelector方法和forwardInvocation方法分为实例方法类方法,这两个方法在NSObject中都有实现.

接下来尝试在最后一次挽救机会中对HQPerosn的实例方法say666进行挽救.

  • 通过methodSignatureForSelector方法,返回say666实例方法方法签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
  • 经过上一步之后,CoreFoundation会创建一个NSInvocation,并NSInvocationmethodSignature设置为上一步的返回值.接下来,需要在forwardInvocation中指定接收消息的接收者,传递的参数等信息.由于本案例没有参数,因此,只设定消息接收者即可.
- (void)forwardInvocation:(NSInvocation *)anInvocation{
    [anInvocation invokeWithTarget:[[HQHuman alloc] init]];
}
  • 尝试向HQPerson对象发送say666消息,查看结果
2021-02-01 16:29:06.990005+0800 HQObjc[38332:2540396] Hello, World!
2021-02-01 16:29:12.188971+0800 HQObjc[38332:2540396] -[HQHuman say666]
2021-02-01 16:29:13.685739+0800 HQObjc[38332:2540396] end~
Program ended with exit code: 0

由结果同样可以看到,HQPerson类虽然没有实现say666的实例方法,但经过慢速消息转发后,将say666消息接收者修改成HQHuman了.最终由HQHuman完成消息的执行.

相关文章

  • OC底层原理12 - 消息转发

    消息转发 经过了快速查找,慢速查找,动态方法解析之后,仍然没有找到SEL对应的IMP,此时我们在抛出异常信息的信息...

  • iOS 底层原理 - 消息转发

    在上一篇 iOS 底层原理 - 消息查找流程中,我们知道OC消息机制分为三个阶段,消息发送,动态解析和消息转发,如...

  • iOS--OC底层原理文章汇总

    OC底层原理01—alloc + init + new原理OC底层原理02—内存对齐OC底层原理03— isa探究...

  • OC-底层原理09—消息转发流程

    iOS--OC底层原理文章汇总[/p/14911da92f74] 在前面两章中介绍了方法消息的处理流程,宏观上来说...

  • OC底层原理14-消息转发机制

    我们在 OC底层原理13-动态方法决议[https://www.jianshu.com/p/a7550ccefae...

  • OC底层消息转发机制

    1. 前言 上一篇文章(OC底层方法的本质、查找流程[https://blog.csdn.net/guoyongm...

  • RxSwift Runtime分析(利用OC消息转发实现IOS消

    RxSwift Runtime分析(利用OC消息转发实现IOS消息拦截)<原理同ReactiveCocoa> Rx...

  • OC底层原理汇总

    OC底层原理(一).alloc实际调用流程分析OC底层原理(二).内存分配与内存对齐OC底层原理(三)、isa、对...

  • OC的消息转发机制

    OC的消息转发机制众所周知,OC中的方法调用是利用消息转发实现的。 首先我们来了解一下类的底层构造如下: objc...

  • iOS的消息转发机制

    一、简介: 消息转发是OC底层一种功能强大的实现,为OC方法的调用增加更多的表现力和容错能力。什么是消息转发?简单...

网友评论

      本文标题:OC底层原理12 - 消息转发

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