【iOS】消息传递和消息转发

23 篇文章 0 订阅
19 篇文章 0 订阅

消息传递和消息转发

objc_msgSend执行流程

OC中的方法调用,其实都是转化为objc_msgSend函数调用,objc_msgSend的执行流程可以分为3大阶段

    1. 消息发送
    1. 动态方法解析
    1. 消息转发

消息发送

在这里插入图片描述

消息发送流程是我们平时最经常使用的流程,其他的像动态方法解析和消息转发其实是补救措施。具体流程如下

1、首先判断消息接受者receiver是否为nil,如果为nil直接退出消息发送
2、如果存在消息接受者receiverClass,首先在消息接受者receiverClass的cache中查找方法,如果找到方法,直接调用。如果找不到,往下进行
3、没有在消息接受者receiverClass的cache中找到方法,则从receiverClass的class_rw_t中查找方法,如果找到方法,执行方法,并把该方法缓存到receiverClass的cache中;如果没有找到,往下进行
4、没有在receiverClass中找到方法,则通过superClass指针找到superClass,也是现在缓存中查找,如果找到,执行方法,并把该方法缓存到receiverClass的cache中;如果没有找到,往下进行
5、没有在消息接受者superClass的cache中找到方法,则从superClass的class_rw_t中查找方法,如果找到方法,执行方法,并把该方法缓存到receiverClass的cache中;如果没有找到,重复4、5步骤。如果找不到了superClass了,往下进行
6、如果在最底层的superClass也找不到该方法,则要转到动态方法解析

动态方法解析

在这里插入图片描述

== 把这两个方法名记住:+resolveInstanceMethod: +resolveClassMethod: ==

  • 开发者可以实现以下方法,来动态添加方法实现
    • +resolveInstanceMethod:
    • +resolveClassMethod:
  • 动态解析过后,会重新走“消息发送” 的流程,从receiverClass的cache中查找方法这一步开始执行

+ (BOOL)resolveInstanceMethod:(SEL)sel {  //sel代表无法响应的方法名
    if (sel == @selector(test)) {
        //获取其他方法
        Method method = class_getInstanceMethod([self class], @selector(other));  //这是一个runtime函数,需要包含头文件#import <objc/runtime.h>,
            //参数一:(class)sel是未能响应的方法的类,就传[self class]
            //参数二(SEL)name是要查找的方法名(就是自己动态添加的方法的方法名),传 @selector(方法名)
        //动态添加test的方法
        class_addMethod([self class], sel, method_getImplementation(method), method_getTypeEncoding(method));//这也是一个runtime函数,用于动态的向一个类添加一个新的方法。
        //参数一:(Class)cls是要添加方法的类,就传[self class]
        //参数二:(SEL)name表示未能相应的方法名,就传sel
        //参数三:(IMP)imp是方法的实现函数指针, 就传method_getImplementation(method)
        //参数四:(const char*)types是方法的类型编码字符串,就传method_getTypeEncoding(method))
    }
    return [super resolveInstanceMethod:sel];
} //系统默认返回NO

可以看出,这个方法的实现大部分内容都是固定的。完全套模版。

如果这个方法没有实现,默认返回NO,并查看有没有实现forwardingTargetForSelector:方法

消息转发

如果一个方法在消息发送阶段没有找到相关方法,也没有进行动态方法解析,这个时候就会走到消息转发阶段了。
在这里插入图片描述

==再记住下面这三个方法:forwardingTargetForSelector , methodSignatureForSelectot , forwardInvocation: ,doesNotRecognizeSelector: ==

  • 调用forwardingTargetForSelector,返回值不为nil时,会调用objc_msgSend(返回值, SEL)
  • 调用methodSignatureForSelector,返回值不为nil,调用forwardInvocation:方法;返回值为nil时,调用doesNotRecognizeSelector:方法
  • 开发者可以在forwardInvocation:方法中自定义任何逻辑
  • 以上方法都有对象方法、类方法2个版本(前面可以是加号+,也可以是减号-)

- (id)forwardingTargetForSelector:(SEL)aSelector {
    if (aSelector == @selector(test)) {
        return [[Student alloc] init];
    }
    return nil;
} //系统默认返回nil
//调用forwardingTargetForSelector,返回值不为nil时,会调用objc_msgSend(返回值, SEL),结果就是调用了objc_msgSend(Student,test)

这里我的理解就是,把这个方法换一个对象来调用,调用成功的前提是,该对象的类实现了此方法名的方法

forwardingTargetForSelector如果没有实现,默认返回nil。进入后续的方法。


- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    if (aSelector == @selector(test)) {
        
        //这三种实现方法都可以
        return [[[Student alloc] init] methodSignatureForSelector:aSelector];
        //return [[Student class] instanceMethodSignatureForSelector:aSelector];
        //return [NSMethodSignature signatureWithObjCTypes:"i@:i"];
    }
    return [super methodSignatureForSelector:aSelector];
}

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

    if ([anInvocation selector] == @selector(test)) {

    } else {

    }

    NSLog(@" 直接在这里写方法实现,这里我想干嘛就干嘛");
}

forwardingTargetForSelector:返回nil,才会进入这一步。
如果methodSignatureForSelector: 获取方法签名成功,则调用forwardInvocation:来转发消息,如果失败,就是经典的doesNotRecognizeSelector:报错
forwardInvocation:方法的使用非常灵活

anInvocation 参数是一个 NSInvocation 对象,它封装了一个方法调用的所有信息,包括:

  • 目标对象 (即将被调用方法的对象)
  • 方法选择器 (即将被调用的方法)
  • 方法参数 (调用方法时传递的参数)
  • 返回值 (方法的返回值)

例如,你可以使用以下方法获取方法选择器和参数:

SEL theSelector = [anInvocation selector];
const char *methodType = [anInvocation methodSignature].methodReturnType;

你还可以设置新的目标对象和参数,然后调用 invokeWithTarget: 方法将消息转发给另一个对象:


[anInvocation setTarget:anotherObject];
[anInvocation setArgument:&someArgument atIndex:2];
[anInvocation invoke];

通过这种方式,你可以将收到的消息转发给其他对象来处理。这在实现动态消息转发的时候非常有用。

另外,anInvocation 对象还提供了一些其他有用的方法,比如 getReturnValue: 和 getArgument:atIndex: 等,可以帮助你获取和设置方法的返回值和参数。

super

待补充

面试题

  1. 什么是消息转发?它是如何工作的?简要回答。

答:消息转发是Objective-C处理未识别消息的机制

当对象无法响应某消息时,会进入消息转发流程:
1. 动态方法解析:通过实现+resolveInstanceMethod:或+resolveClassMethod:动态添加方法。
2. 备用接收者:通过-forwardingTargetForSelector:指定另一个对象处理消息。
3. 完整消息转发:通过-methodSignatureForSelector:和-forwardInvocation:完整转发消息。

  1. 什么是Objective-C中的消息传递?与C++中 的方法调用有什么区别?底层实现过程?

答:

  • 在Objective-C中,消息传递是对象间通信的方式。发送消息的语法为[object message],底层实现是将消息发送给对象的isa指针指向的类,在方法列表中查找对应的方法实现。
  • 与C++中的方法调用区别:
    Objective-C通过运行时系统实现消息传递,方法绑定在运行时完成。
    C++通过编译时绑定(静态绑定),方法在编译时确定。
  • 底层实现过程:
    消息传递底层实现通过objc_msgSend函数:
      1. 查找方法:根据对象的isa指针,找到类的method_list,查找对应的SEL(选择器)。
      1. 执行方法:找到方法实现后,执行该方法。如果找不到,进入消息转发机制。
  • 26
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值