Objective-c 初阶 —— Runtime(方法交换 消息传递)

一、消息传递

1、什么是消息

[a func1];

我们会把这种用方括号来调函数的方式称为发消息。对于这个例子,就相当于我们给 a 这个对象发了个 func1 的消息(个人认为指令更好理解)。

2、什么是 selector

selector 就是一个函数区分器。它只会给这个方法名一个唯一的哈希值。也就是说,如果只要两个函数的方法名是一样的,那么他们的 selector 的哈希值就是一样的。

3、什么是 isa 指针

每个对象都有一个 isa 指针。

实例对象的 isa 指针指向的是该实例所属的类对象,而类对象的 isa 指针指向元类对象。

那么如何区分类对象和元类对象呢?类对象存的是实例方法,不存类方法,而元类存的是类方法。

4、消息传递过程

1. 当一个方法要传给一个实例对象,那么 runtime 系统就会通过这个实例对象的 isa 指针找到该对象属于哪个类对象

2. 在这个类对象里的 dispatch table 找有没有符合当前 selector 的函数实现

3. 如果有,直接调用。

4. 如果没有,就不断地沿着这个类对象的 superclass 指针一路沿着继承链向上找,直到找到符合的函数实现或找到 NSObject 类。

5. 如果直到 NSObject 类都没找到,就进入“消息转发”的环节。

根据 selector 找对应的函数实现,runtime 做了一个小小的优化。就是给每个类对象一个缓存。这个缓存存的是在该类找过的实例方法实现和该类通过继承得来的函数实现的地址。于是在找函数实现时,runtime 会先在这个类的缓存里找。如果没找到才去这个类的 dispatch table 里找;再找不到就沿着继承链往上找。

二、消息转发的过程

1、什么是消息转发

消息转发就是当 runtime 在继承链中找不到 selector 对应的函数实现后,就会进行消息转发,即用其他对象来调这个方法。如果没有其他类能成功调用这个方法,才会报错。所以可以理解成消息转发就是报错前的最后一道防线。

2、消息转发的过程

2.1. 动态方法解析(resolveInstanceMethod:)

+ (BOOL)resolveInstanceMethod:(SEL)selector //实例对象的动态方法解析调+ (BOOL)resolveClassMethod:(SEL)selector //类对象的动态方法解析调
@implementation Person// 动态方法解析入口:当找不到实例方法 sayHello 时会调用这个方法
+ (BOOL)resolveInstanceMethod:(SEL)sel {if (sel == @selector(sayHello)) {// 使用 class_addMethod 动态添加方法实现class_addMethod(self, sel, (IMP)dynamicSayHello, "v@:");return YES;}return [super resolveInstanceMethod:sel];
}// 动态添加的方法实现
void dynamicSayHello(id self, SEL _cmd) {NSLog(@"Hello from dynamic method!");
}@end

 其中 selector 是未处理的方法。 返回值表示能否新增一个方法来处理。如果可以,返回 YES;否则就看看基类能不能处理这个 selector。

2.2. 备用接收者

- (id)forwardingTargetForSelector:(SEL)aSelector;
// 备用接收者类
@interface BackupHandler : NSObject
- (void)sayHello;
@end@implementation BackupHandler
- (void)sayHello {NSLog(@"👉 BackupHandler 收到了 sayHello 消息!");
}
@end// 原始类:没有实现 sayHello,但可以转发给 backup
@interface MainObject : NSObject
@property (nonatomic, strong) BackupHandler *backup;
@end@implementation MainObject// 快速消息转发:返回备用接收者
- (id)forwardingTargetForSelector:(SEL)aSelector {if (aSelector == @selector(sayHello)) {return self.backup; // 转发给备用对象}return [super forwardingTargetForSelector:aSelector];
}@end

 这里 selector 也是未处理的方法。返回值为当前找到的备援接受者,如果没有则返回nil,进入下一阶段。

2.3. 完整消息转发

- (void)forwardInvocation:(NSInvocation *)anInvocation;
// 目标处理者类
@interface RealHandler : NSObject
- (void)sayHello;
@end@implementation RealHandler
- (void)sayHello {NSLog(@"✅ RealHandler 处理了 sayHello 方法!");
}
@end// 原始类:完全不实现 sayHello
@interface MainObject : NSObject
@property (nonatomic, strong) RealHandler *handler;
@end@implementation MainObject// 第一步:提供方法签名,告诉 runtime 方法是怎样的
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {if (aSelector == @selector(sayHello)) {// v@: → 返回 void, 参数是 self 和 _cmdreturn [NSMethodSignature signatureWithObjCTypes:"v@:"];}return [super methodSignatureForSelector:aSelector];
}// 第二步:收到完整调用对象,自己决定怎么处理
- (void)forwardInvocation:(NSInvocation *)anInvocation {SEL sel = [anInvocation selector];if ([self.handler respondsToSelector:sel]) {[anInvocation invokeWithTarget:self.handler]; // 手动转发} else {[super forwardInvocation:anInvocation]; // 没法处理就崩溃}
}@end

 其中 anInvocation 里面有原来消息的接收者、selector 、全部实参、返回值。

2.4. 报错中断

三、方法交换

1、dispatch table

每个类结构都包括以下两个基本元素:指向基类的指针 & dispatch table。其中 dispatch table 就是一个 2 列的函数表,左边是某个函数的 selector,右边是这个函数的具体实现的地址。 

2、方法交换

本质就是把 dispatch table 里的两个 address 的值交换。

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

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

相关文章

【计算机网络架构】树型架构简介

引言在当今数字化时代,网络架构如同复杂的神经系统,支撑着各种信息的流通与交互。从个人日常的网络浏览、在线购物,到企业的远程办公、数据存储,再到国家层面的政务信息化、智慧城市建设,网络架构都扮演着不可或缺的角…

llama-factory快速开始

llama-factory快速开始 文章目录llama-factory快速开始前言一、环境配置1.1 训练顺利运行需要包含4个必备条件1.2 llama-factory下载1.3 环境下载1.4 硬件环境校验二、启动前言 https://github.com/hiyouga/LLaMA-Factory/blob/main/README_zh.md这是GitHub中文介绍文档&#…

408数据结构强化(自用)

常用代码片段&#xff08;持续更新&#xff09;折半查找void SearchBinary(int A[];int x){int low 0, high n-1, mid;while(low<high){mid (lowhigh)/2;if(A[mid]x) break;else if(A[mid] < x) low mid 1;else high mid - 1;}顺序表逆置void Reverse(SqList &…

linux cpu频率和AVS调压等级

1&#xff0c;linux常见的cpu频率对应的电压等级对应参数表如下:频率&#xff08;GHz&#xff09;电压&#xff08;V&#xff09;1.61.41.41.21.21.01.00.82&#xff0c;avs调压的几种方式linux内核宏解释Linux内核中&#xff0c;AVS调压的实现依赖于一些宏定义和配置选项&…

Input输入和Screen相关

知识点using System.Collections; using System.Collections.Generic; using UnityEngine;public class Lesson11 : MonoBehaviour {// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){#region 注意&#xff0c…

如何在CSDN变现?如何赚钱?如何涨粉?如何找到优质大V博主合作伙伴?

&#x1f525; 2025最新 如何在CSDN变现&#xff1f;如何赚钱&#xff1f;如何跟对人&#xff1f;如何找到优质博主合作伙伴&#xff1f; 大家好&#xff0c;我是猫头虎&#xff0c;今天✍️想和大家聊聊在CSDN平台变现的问题。这也是绝大多数伙伴非常关心的一个话题——其实&…

OpenCV特征点提取算法orb、surf、sift对比

下面是 OpenCV 中三种常用特征点提取算法&#xff1a;ORB、SURF 和 SIFT 的详细对比&#xff0c;从 算法原理、性能、使用限制 和 适用场景 多维度进行总结&#xff0c;帮助大家在实际项目中合理选择。一览表&#xff1a;ORB vs. SURF vs. SIFT属性/算法ORBSURFSIFT全称Oriente…

LeafletJS 与 React:构建现代地图应用

引言 LeafletJS 是一个轻量、灵活的 JavaScript 地图库&#xff0c;广泛用于创建交互式 Web 地图&#xff0c;而 React 作为现代前端框架&#xff0c;以其组件化、状态管理和虚拟 DOM 特性&#xff0c;成为构建动态用户界面的首选工具。将 LeafletJS 与 React 结合&#xff0c…

前后端数据交互,关于表单数据传输问题

表单提交var formData new FormData(); // 添加每个事故ID作为单独的参数 accidentIds.forEach(id > formData.append(accidentIds, id)); formData.append(status, statusText); $.messager.confirm(确认, 确定要将事故记录标记为 statusText 吗&#xff1f;, function …

新书推介 | 吉林大学出版教材《汽车智能辅助驾驶系统技术》,国产仿真工具链GCKontrol-GCAir教学应用

近日&#xff0c;吉林大学出版了由高镇海教授、孙天骏副教授主编的新教材《汽车智能辅助驾驶系统技术》&#xff0c;本书系统地介绍了汽车智能辅助驾驶系统的发展需求、物理架构、功能算法、技术原理以及应用场景。在教材第17章《仿真测试》&#xff0c;应用国产化GCKontrol-GC…

从 0 到 1 玩转 XSS - haozi 靶场:环境搭建 + 全关卡漏洞解析

文章目录前言靶场地址0X00 直接注入0X01 闭合标签10X02 闭合标签20X03 绕过特殊符号10X04 绕过特殊符号20X05 绕过注释符0X06 绕过更多符号0X07 绕过更多符号20X08 绕过闭合符号0X09 绕过URL匹配0X0A 绕过URL过滤0X0B 绕过大写转换10X0C 绕过大写转换20X0D 绕过注释0X0E 古英语…

TF卡格式化

cmd 输入diskpart,在新打开的窗口输入list disk&#xff0c;然后select disk [磁盘号]&#xff0c;clean&#xff0c;回车变成未分区的。再选中磁盘&#xff0c;选中之后create partition [分区名] 回车&#xff0c;list partition&#xff0c;查看分区&#xff0c;输入active&…

Python爬虫实战:研究sqlparse库相关技术

1. 引言 1.1 研究背景与意义 在当今数据驱动的时代,SQL 作为关系型数据库的标准查询语言,被广泛应用于各种数据处理和分析场景。随着数据库应用的不断发展,SQL 代码的规模和复杂度也在不断增加,这给 SQL 代码的编写、维护和优化带来了挑战。 研究表明,低效的 SQL 查询是…

全球天气预报5天(经纬度版)免费API接口教程

本文全面介绍由接口盒子免费API提供的全球天气预报API&#xff0c;支持通过经纬度坐标获取任意地区未来5天的详细天气预报数据。 一、接口核心功能 ​全球覆盖​&#xff1a;支持全球任意经纬度坐标点的天气预报​高精度预报​&#xff1a;提供每3小时为间隔的精细化预报&…

5 基于STM32单片机的绝缘检测系统设计(STM32代码编写+手机APP设计+PCB设计+Proteus仿真)

系列文章目录 文章目录 系列文章目录前言1 1 资料获取与演示视频1.1 资料介绍1.2 资料获取1.3 演示视频 2 系统框架3 硬件3.1 主控制器3.2 显示屏3.3 WIFI模块3.4 DHT11温湿度传感器3.5 可调电位器 4 设计PCB4.1 安装下载立创EDA专业版4.2 画原理图4.3 摆放元器件&#xff0c;设…

CPP学习之list使用及模拟实现

一、list简介及用法 1. list简介 list是可以在常数范围内任意位置进行插入、删除、修改操作的有顺序性的容器&#xff0c;而且支持双向迭代&#xff0c;其底层是双链表结构&#xff0c;逻辑上连续但物理空间上不连续&#xff0c;只能通过指针来进行元素访问&#xff0c;无法使用…

Spring Boot 参数校验:@Valid 与 @Validated

在日常开发中&#xff0c;参数校验是保障接口健壮性与数据安全的第一道防线。Spring Boot 为我们提供了基于 JSR-303/JSR-380 的强大校验机制&#xff0c;通过注解与 AOP 实现了灵活且高效的数据校验方式。本篇博客将详细介绍 Spring Boot 中 Valid、Validated 注解的使用方法&…

linux看门狗重启定位思路总结

1&#xff0c;看门狗定位思路&#xff08;1&#xff09;是否是死锁导致查看日志查看是否有RCU install或者deadlock相关打印&#xff0c;如果有的话可以考虑使用lockdep死锁检测工具&#xff08;2&#xff09;中断风暴查看中断&#xff0c;抓中断打印&#xff0c;可以查看/proc…

基于单片机直流电机测速中文液晶显示设计

摘 要 在现在工业自动化高度发展的时期&#xff0c;几乎所有的工业设备都离不开旋转设备&#xff0c;形形色色的电机在不同领域发挥着很重要的作用。不同场合对电机控制要求是不同的&#xff0c;但大部分都会涉及到旋转设备的转速测量&#xff0c;从而利用转速来实施对旋转设备…

c# sqlsugar 主子表明细 查询

在使用 SqlSugar ORM 进行数据库操作时&#xff0c;特别是在处理主子表关系时&#xff0c;通常需要执行关联查询来获取主表和其子表的数据。SqlSugar 提供了强大的查询能力&#xff0c;支持多种方式的关联查询&#xff0c;包括左连接&#xff08;Left Join&#xff09;、内连接…