笔记-iOS消息转发机制和使用

news2024/11/24 15:29:51

消息转发机制的回顾以及涉及的几个方法

一、OC消息发送原理 + 消息转发机制

1、由于OC的动态特性,只有当程序运行起来之后,才知道要真正执行哪个函数(动态绑定)。在编译过程向类发送了其无法理解的消息并不会报错,因为在运行时,我们可以改变对象调用的方法、向类中添加方法。

2、OC消息发送原理、方法查找过程

(1)调用一个方法(包括respondsToSelector),编译器将OC代码,转换成C函数,给对象发送消息 : void objc_msgSend(id self, SEL cmd,…) ,第一个参数是接收者,第二个参数是方法(名),后面是消息的参数。

(2)objc_msgSend查找方法的过程:

实例对象根据其isa指针,找到其所属的class,然后遍历其methodLists,如果找到则根据IMP函数指针去调用,并且缓存(objc_cache);如果没有找到,那么根据这个类的super_class找到其父类,再看其父类是否能相应这个方法就可以了,直到super_class为nil时,就无法响应这个方法了,此时就触发消息转发机制。
当使用类名调用类方法(+方法)时,只需要根据class的isa指针,找到其meta-class,然后通过meta-class的methodLists找到相应的方法既可(“类”是“元类”的对象)。

3、如果对象接收到无法解读的消息后(未查询到该方法),就会启动“消息转发”机制,我们可在此过程告诉对象应该如何处理未知消息。如果我们不做任何处理,或处理无效,则会调用doesNotRecognizeSelector:,造成异常崩溃:unrecognized selector sent to instance 0xxx

二、消息转发机制的处理过程

消息转发机制依次的三个过程:

1、动态方法解析

第一阶段,先征询接收者所属的类,是否需要动态的添加方法,用来处理当前未找到的方法。对象在无法解读消息时会首先调用所属类的下列类方法,来判断是否能接收消息:

+ (BOOL) resolveInstanceMethod:(SEL)selector,参数为那个未知的选择子,返回值表示这个类能否新增一个实例方法处理此选择子。
如果是类方法 ,则调用  + (BOOL) resolveClassMethod:(SEL)selector,有一点要注意,类方法的添加需要在其“元类”里面。

举例:

//消息转发机制的第一步 :动态方法解析
+ (BOOL)resolveInstanceMethod:(SEL)sel{
    NSString *selName = NSStringFromSelector(sel);
    if ([selName hasPrefix:@"doSomeThing"]) {//判断特定无法响应的方法
        class_addMethod(self, sel, (IMP)otherOneDoSomeThing, "v@:");//动态添加响应方法
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

//动态将实现转到这个函数(或者就是单纯的添加doSomeThing方法)
void otherOneDoSomeThing(id self ,SEL _cmd){
    NSLog(@"class:%@, sel:%s",self,sel_getName(_cmd));
    NSLog(@"原对象无法响应该消息,在动态方法解析时添加了一个方法来处理该消息");
}

2、备用的接收者

第二阶段,如果动态方法解析没有发现添加的方法,那么尝试转发给其他对象来处理这个方法。该步骤调用的方法是:

- (id) forwardingTargetForSelector:(SEL)selector

举例:

- (id)forwardingTargetForSelector:(SEL)aSelector{
    NSString * selString = NSStringFromSelector(aSelector);
    if([@"doSomeThing" isEqualToString:selString]){
        OtherObject *someone = [[OtherObject alloc] init];//备选对象
        if ([someone respondsToSelector:aSelector]) {
            return someone;//如果可以响应该方法,则直接转交新对象处理
        }
    }
    return [super forwardingTargetForSelector:aSelector];//如果无合适的备选对象,则继续转发
}

3、完整的消息转发机制

第三阶段,如果没有可用的备选者,那么系统就会把消息所有相关内容封装成一个NSInvocation对象,再做最后的尝试,启动完整的消息转发。先调用methodSignatureForSelector:获取方法签名,然后再调用forwardInvocation:进行处理,这一步的处理可以直接转发给其它对象,即和第二步的效果等效,但是很少有人这么干,因为消息处理越靠后,就表示处理消息的成本越大,性能的开销就越大。所以,在这种方式下,一般会改变消息内容,比如增加参数,改变选择子等等,具体根据实际情况而定。

 - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
 - (void)forwardInvocation:(NSInvocation *)anInvocation

举例

//获取方法签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    NSString *method = NSStringFromSelector(aSelector);
    if ([@"doSomeThing" isEqualToString:method]) {
        /* 手动创建签名
         写法例子一  v@:@
         字符说明:(1)v:返回值类型void;2)@:id类型,执行sel的对象;3): SEL;4)@:参数
         
         写法例子二  @@:
         字符说明:(1)@:返回值类型id;(2)@:id类型,执行sel的对象;(3):SEL
        
         */
        NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:"v@:@"];
        return signature;
    }
    return nil;
}

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    //*-----------  处理方式一:不改变sel -------------*/
    // 拿到这个消息
    SEL selector = [anInvocation selector];
    // 转发消息
    AnotherObject *otherObject = [[AnotherObject alloc] init];
    if ([otherObject respondsToSelector:selector]) {
        // 调用这个对象,进行转发
        [anInvocation invokeWithTarget:otherObject];
    } else {
        [super forwardInvocation:anInvocation];
    }
    //*---------------------------------------------*/

    //*-----------  处理方式二:改变sel -------------*/
    SEL selector = @selector(myAnotherMethod:);
    NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:"v@:@"];
    anInvocation = [NSInvocation invocationWithMethodSignature:signature];
    [anInvocation setTarget:self];
    [anInvocation setSelector:@selector(myAnotherMethod:)];
    NSString *param = @"参数";
    // 消息的第一个参数是self,第二个参数是选择子,所以"参数"是第三个参数
    [anInvocation setArgument:&param atIndex:2];
    
    if ([self respondsToSelector:selector]) {//如果自己响应,就自己处理
        [anInvocation invokeWithTarget:self];
        return;
    } else {
        AnotherObject * otherObject = [[AnotherObject alloc] init];
        if ([otherObject respondsToSelector:selector]) {//交给另外的对象来处理
            [anInvocation invokeWithTarget:otherObject];
            return;
        }
    }
    [super forwardInvocation:anInvocation];
    //*---------------------------------------------*/
}

//类中的另一个方法,来处理消息
- (void)myAnotherMethod:(NSString*)para
{
    NSLog(@"交给我自己的另一个方法来处理:%@", para);
}

三、应用场景

1、 为@dynamic实现方法
使用 @synthesize 可以为 @property 自动生成 getter 和 setter 方法(现 Xcode 版本中,会自动生成),而 @dynamic 则是告诉编译器,不用生成 getter 和 setter 方法。当使用 @dynamic 时,我们可以使用消息转发机制,来动态添加 getter 和 setter 方法。当然你也用其他的方法来实现。

2、间接实现多继承
Objective-C本身不支持多继承,这是因为消息机制名称查找发生在运行时而非编译时,很难解决多个基类可能导致的二义性问题,但是可以通过消息转发机制在内部创建多个功能的对象,把不能实现的功能给转发到其他对象上去,这样就做出来一种多继承的假象。转发和继承相似,可用于为OC编程添加一些多继承的效果,一个对象把消息转发出去,就好像他把另一个对象中放法接过来或者“继承”一样。消息转发弥补了objc不支持多继承的性质,也避免了因为多继承导致单个类变得臃肿复杂。

3、实现多重代理
利用消息转发机制可以无代码侵入的实现多重代理,让不同对象可以同时代理同个回调,然后在各自负责的区域进行相应的处理,降低了代码的耦合程度。
这里就是利用了消息转发机制的第三个阶段,将NSIvocation分发给多个代理去响应。
https://blog.csdn.net/kingjxust/article/details/49559091

4、JSPatch --iOS动态化更新

JSPatch,通过消息转发机制来进行JS和OC的交互,从而实现iOS的热更新。

虽然苹果大力整改热更新让JSPatch的审核通过率在有一段时间里面无法过审,但是后面bang神对源码进行代码混淆之后,基本上是可以过审了。

下面截图只摘出来用到消息转发的部分:关键点就是在第三阶段,通过invocation拿到方法参数,然后传给JS,调用JS的实现函数。

四、总结

1、消息转发机制的时机图示

在这里插入图片描述

2、简单理解

(1)首先,若对象无法响应某个方法调用,则进入消息转发流程。

(2)开始第一步,通过运行时的动态方法解析,可以将需要的某个方法,加入到类中。

(3)上一步失败,开始第二步,将消息转发给其他对象处理。

(4)上述两步失败,启动完整的消息转发机制,通过封装NSInvocation,明确指出方法的响应者(甚至改变SEL)。

(5)上述都失败,抛出异常。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1685938.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于双向长短期记忆BiLSTM对消费者投诉进行多类分类

前言 系列专栏:【深度学习:算法项目实战】✨︎ 涉及医疗健康、财经金融、商业零售、食品饮料、运动健身、交通运输、环境科学、社交媒体以及文本和图像处理等诸多领域,讨论了各种复杂的深度神经网络思想,如卷积神经网络、循环神经网络、生成对抗网络、门控循环单元、长短期记…

FTP如何端口映射?

FTP(File Transfer Protocol)是一种用于在网络上进行文件传输的协议。在FTP协议中,客户端和服务器通过不同的端口进行通信,其中控制连接使用端口号21,数据连接使用端口号20。由于网络环境的限制,一些情况下…

协变(List泛型作为方法参数时的父类子类问题)

有段时间没搞.net的项目了(没办法,谁让国内JAVA流行是事实)。最近又回归.net(哪里需要哪里搬~)。 接收到需求后,一顿输出,结果…咦?编译失败??? 错误信息: CS1503:参数1:无法…

Django介绍:探索Python最受欢迎的Web框架

文章目录 Django是什么Django的核心特性1. MTV架构2. 自带的Admin后台管理系统3. ORM(对象关系映射)4. 强大的表单处理5. 完善的文档和活跃的社区 快速入门:使用Django创建一个简单的Web应用步骤1:安装Django步骤2:创建…

lambdastream

lambda 匿名函数 为了简化java的匿名内部类 事件监听ActionListener 接口(外部类) 内部类 类在其他地方用不到,索性就把这个类定义在类的内部使用 好处:1.内部可以使用外部类成员 2.其他地方发现不了 往往内部类只用一次&…

电力巡检穿戴式智能手环:让巡检不孤立无援

电力巡检穿戴式智能手环:让巡检不孤立无援 在电力巡检的广袤天地里中,电力工作人员他们身着工装,头戴安全帽,手持仪器,穿梭在高压线路与铁塔之间。他们的健康状态,直接关系到电力作业的安全与效率。如今,电…

【投稿资讯】区块链会议CCF A -- SP 2025 截止6.6、11.14 附录用率

会议名称:46th IEEE Symposium on Security and Privacy( S&P) CCF等级:CCF A类学术会议 类别:网络与信息安全 录用率:2023年 195/1147,2024年录用了17篇和区块链相关的论文 Topics of interest inc…

JWT的详解

一.什么是JWT JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在网络应用间安全地传递信息。它是一种紧凑的、自包含的方式,用于在用户和服务之间以 JSON 对象的形式安全地传输信息。 JWT 主要由三部分…

百度智能云参与信通院多项边缘计算标准编制,「大模型时代下云边端协同 AI 发展研讨会」成功召开

1 中国信通院联合业界制定、发布多项标准化成果,推动产业发展 大模型开启了 AI 原生时代,云边端协同 AI 构建了「集中式大规模训练」、「边缘分布式协同推理」新范式,有效降低推理时延和成本,提升数据安全和隐私性,也…

源网络地址转换SNAT

左上角的是访问互联网发送的数据包,第一个是访问,第二个是网页传回来的 3、4项是源端口号和目的端口号(3是随机的(1024-65535),那个是http的网页服务端口就是80) 那么往回传数据的时候源和目的…

美国教育数据分析

文章目录 第1关:认识数据第2关:数据预处理第3关:数学成绩预测 第1关:认识数据 编程要求 根据提示,在右侧Begin-End区域补充代码,查看数据属性名称。 测试说明 程序会调用你实现的方法,查看数据…

Facebook隐私保护:数据安全的前沿挑战

在数字化时代,随着社交媒体的普及和应用,个人数据的隐私保护问题日益受到关注。作为全球最大的社交平台之一,Facebook承载了数十亿用户的社交活动和信息交流,但与此同时,也面临着来自内外部的数据安全挑战。本文将深入…

计算机视觉SCI期刊,中科院2区,专业认可度高!

一、期刊名称 Displays 二、期刊简介概况 期刊类型:SCI 学科领域:计算机科学 影响因子:4.3 中科院分区:2区 三、期刊征稿范围 这本期刊接受有关显示技术和应用的技术,原创研究论文和教程论文。该期刊欢迎广泛的显…

基于 vLLM 搭建 DeepSeek-V2 Chat 服务

直奔主题。 安装vLLM 官方实现的代码还没有 merge 到 vLLM 主分支,所以直接 git clone DeepSeek 的分支。 git clone https://github.com/zwd003/vllm.git cd vllm pip install -e .源码安装大概耗时 10 分钟。 OpenAI 接口规范启动 官方 Github 放的是单条推理…

吉时利Keithley 2010数字万用表7.5 位

Keithley 2010数字万用表,7.5 位 吉时利 2010 数字万用表、7.5 位、低噪声万用表将高分辨率与生产应用所需的高速度和高准确度相结合,例如测试精密传感器、换能器、A/D 和 D/A 转换器、调节器、参考、连接器、开关和继电器。2010 基于与吉时利 2000、20…

Flink DataStream API 基础算子(一)

一、介绍 官网 DataStream API 得名于特殊的 DataStream 类,该类用于表示 Flink 程序中的数据集合。你可以认为 它们是可以包含重复项的不可变数据集合。这些数据可以是有界(有限)的,也可以是无界(无限)的…

k8s node NotReady后会发生什么?

K8s 是一种强大的容器编排和管理平台,能够高效地调度、管理和监控容器化应用程序;其本身使用声明式语义管理着集群内所有资源模型、应用程序、存储、网络等多种资源,Node 本身又属于 K8s 计算资源,上面承载运行着各种类型的应用程…

selenium环境安装和web自动化基础

webUI自动化背景 因为web页面经常会变化,所以UI自动化测试的维护成本很高。不如接口的适用面广,所以大部分公司会做接口自动化测试,但是未必会做UI自动化测试; UI自动化测试要做也是覆盖冒烟测试,不会到很高的覆盖率&a…

gpt-4o考场安排

说明 :经过多次交互,前后花了几个小时,总算完成了基本功能。如果做到按不同层次分配考场,一键出打印结果就完美了。如果不想看中间“艰苦”的过程,请直接跳到“最后结果”及“食用方法”。中间过程还省略了一部分交互&…

集中抄表系统是什么?

1.集中抄表系统简述 集中抄表,又称为智能抄表,是一种现代化能源管理体系技术性,主要运用于电力工程、水、气等公共事业的计量。它通过自动化的形式收集解决大量用户的计量数据信息,大大提升了数据收集的效率和精确性,…