Runtime系列:消息机制【04】

简介: OC中的方法调用的本质,都是转换为objc_msgSend函数的调用。这里所说的消息机制就是objc_msgSend的执行流程。

OC中的方法调用的本质,都是转换为objc_msgSend函数的调用。

这里所说的消息机制就是objc_msgSend的执行流程。


objc_msgSend的执行流程可以分为3大阶段:


1、消息发送


1、首先判断消息接收者是否为nil,如果为nil则直接退出。所以存在使用 nil 调用方法,编译的时候也不会报错。

2、如果消息接收者不为空,通过isa指针找到消息接收者类对象,然后去查找方法(如果类对象没有找到方法,通过superclass指针找到父类继续查询),具体流程如下图:


receiver通过isa指针找到receiverClass

5bab06cd1b66475aacc5c19dc7a02383.png


注意:如果是在class_rw_t中查找方法:


1、已经排序的,二分查找

2、没有排序的,遍历查找


如果消息接收者的类和所有父类中都没有找到方法实现。则进入动态方法解析阶段。


2、动态方法解析


底层实现代码:


_class_resolveMethod(cls,sel,inst);

a53eda5f9d714ed89b45db2ce7aea3a3.pngtriedResolver:是否动态解析的标记。



2.1、实例方法动态解析

方法一:


//-(void)test {
//    NSLog(@"%s",__func__);
//}
- (void)other {
    NSLog(@"%s", __func__);
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(test)) {
        // 获取其他方法
        Method method = class_getInstanceMethod(self, @selector(other));
        // 动态添加test方法的实现
        class_addMethod(self,
                        sel,
                        method_getImplementation(method),
                        method_getTypeEncoding(method)
                        );
        // 返回YES代表有动态添加方法
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}


注意:


Method 可以理解为等价于struct method_t *

方法二:


//- (void)test {
//    NSLog(@"%s",__func__);
//}
void c_other(id self, SEL _cmd) {
    NSLog(@"c_other - %@ - %@", self, NSStringFromSelector(_cmd));
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(test)) {
        // 动态添加test方法的实现
        class_addMethod(self,
                        sel,
                        (IMP)c_other,
                        "v16@0:8"
                        );
        // 返回YES代表有动态添加方法
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}


方法实现:


2.2、类方法动态解析

//+ (void)test {
//    NSLog(@"%s",__func__);
//}
void c_other(id self, SEL _cmd) {
    NSLog(@"c_other - %@ - %@", self, NSStringFromSelector(_cmd));
}
+ (BOOL)resolveClassMethod:(SEL)sel {
    if (sel == @selector(test)) {
        // 注意:第一个参数是类对象【object_getClass(self)】
        class_addMethod(object_getClass(self), sel, (IMP)c_other, "v16@0:8");
        return YES;
    }
    return [super resolveClassMethod:sel];
}


3、消息转发


底层实现代码:


imp = (IMP)_objc_msgforward_impcache;

98edd44f93d6479f838f95edfaddbfa2.png


开发者可以在forwardInvocation:方法中自定义任何逻辑

以上方法都有对象方法、类方法2个版本(前面可以是加号+,也可以是减号-)


解决方案:


3.1、实例方法

1、将消息转发给另一个实例对象实现

本质:在另外一个实例对象,实现一个一样的方法


- (id)forwardingTargetForSelector:(SEL)aSelector {
    if (aSelector == @selector(test)) {
        // objc_msgSend([[Cat alloc] init], aSelector)
        return [[Cat alloc] init];
    }
    return [super forwardingTargetForSelector:aSelector];
}


Cat.h:


#import <Foundation/Foundation.h>
@interface Cat : NSObject
- (void)test;
@end


Cat.m:


#import "Cat.h"
@implementation Cat
- (void)test
{
    NSLog(@"%s", __func__);
}
@end


2、方法签名

// 方法签名:返回值类型、参数类型
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    if (aSelector == @selector(test)) {
        return [NSMethodSignature signatureWithObjCTypes:"v16@0:8"];
        // return [[[Cat alloc] init] methodSignatureForSelector:aSelector];
    }
    return [super methodSignatureForSelector:aSelector];
}
/**
 * NSInvocation封装了一个方法调用,包括:方法调用者、方法名、方法参数
 * anInvocation.target 方法调用者
 * anInvocation.selector 方法名
 * [anInvocation getArgument:NULL atIndex:0]
 */
- (void)forwardInvocation:(NSInvocation *)anInvocation {
    // 参数顺序:receiver、selector、other arguments
    // anInvocation.target == [[MJCat alloc] init]
    // anInvocation.selector == test:
    // anInvocation的参数:15
    // [[[Cat alloc] init] test:15]
    [anInvocation invokeWithTarget:[[Cat alloc] init]];
    // 返回值
    int ret;
    [anInvocation getReturnValue:&ret];
    NSLog(@"%d", ret);
}


3.2、类方法

1、将消息转发给另一个实例对象实现

+ (id)forwardingTargetForSelector:(SEL)aSelector {
    // objc_msgSend([[Cat alloc] init], @selector(test))
    // [[[Cat alloc] init] test]
    if (aSelector == @selector(test)) {
        return [[Cat alloc] init];
    }
    return [super forwardingTargetForSelector:aSelector];
}


2、方法签名

+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    if (aSelector == @selector(test)) {
        return [NSMethodSignature signatureWithObjCTypes:"v@:"];
    }
    return [super methodSignatureForSelector:aSelector];
}
+ (void)forwardInvocation:(NSInvocation *)anInvocation {
    NSLog(@"1123");
}


当以上三个阶段没有实现,那就是 objc_msgSend 找不到合适的方法进行调用,会报错unrecognized selector sent to instance。


以上是从消息发送后,消息处理的全部流程。


相关文章
|
存储
13-iOS消息转发机制以及常用场景
13-iOS消息转发机制以及常用场景
518 0
|
监控 API 开发工具
HarmonyOS Next的HiLog日志系统完全指南:从入门到精通
本文深入解析HarmonyOS Next的HiLog日志系统,涵盖日志级别、核心API、隐私保护与高级回调功能,助你从入门到精通掌握这一重要开发工具。
1031 1
|
10月前
|
数据采集 人工智能 运维
数据治理系统如何赋能企业?建设路径与成本全解析(2025年12月更新)
在AI与大数据时代,数据治理已成为企业业务增长的核心引擎。本文深度解析瓴羊Dataphin、字节Dataleap、奇点云DataSimba等主流系统,从赋能价值、建设路径、产品对比到成本测算,全面指导企业科学选型。尤其推荐基于阿里十年实践的瓴羊Dataphin,其以智能自动化、业务驱动和全链路能力,助力企业打破数据孤岛、提升数据质量、实现安全合规,推动数智转型与业务增值。
|
缓存 安全 网络协议
HTTP和HTTPS的区别有哪些?
本文简要总结了 HTTP 和 HTTPS 的区别,从概念、端口、连接方式、使用场景、安全性等多个角度进行了对比。HTTP 是无状态的、无连接的应用层协议,适用于一般性网站和性能要求较高的应用;HTTPS 则通过 SSL/TLS 层提供加密、认证和完整性保护,适用于涉及敏感信息和高安全性的场景。文章还讨论了两者在性能上的差异,包括握手和加密开销、缓存效果以及 HTTP/2 的多路复用技术。最终,根据具体需求选择合适的协议能够更好地平衡安全性和性能。
18802 2
HTTP和HTTPS的区别有哪些?
|
人工智能 自然语言处理 机器人
数字化呼叫中心运营升级:效率提升与成本优化的实践方法
本文详细阐述了数字化呼叫中心运营升级的核心路径。首先,文章解析了传统呼叫中心面临的痛点,并提出了“以客户为中心”的数字化转型理念。接着,重点剖析了全渠道融合、智能客服应用、自动化质检与数据分析三大关键升级实践。通过具体的案例,文章展示了如何通过这些方法有效提升服务效率、优化客户体验,并显著降低运营成本。最后,为企业提供了具体的厂商选型与实施建议,旨在帮助企业平稳完成数字化转型。
691 22
|
SQL 存储 监控
MySQL 内置的监控工具介绍及使用篇(一)
MySQL 内置的监控工具介绍及使用篇
3209 0
|
存储 供应链 监控
Python 实战:打造智能进销存系统
Python 实战:打造智能进销存系统
1176 0
|
Rust IDE 安全
30天拿下Rust之环境搭建
Rust是一门注重安全与速度的现代系统级编程语言,凭借其独特的内存安全保证和卓越性能,在系统编程、WebAssembly等领域崭露头角。本文引导您完成Rust开发环境搭建,包括通过Rustup安装Rust及其工具链,并介绍如何使用Cargo——Rust的包管理和构建工具。此外,还推荐了几款支持Rust的集成开发环境(IDE),如RustRover、IntelliJ IDEA/CLion及VSCode等。最后,通过创建并运行一个简单的“Hello, world!”程序,让您亲身体验Rust编程的魅力。
875 6
|
算法 C语言
【C语言】:atoi函数的使用及其模拟实现
【C语言】:atoi函数的使用及其模拟实现
878 5
|
应用服务中间件 Linux 定位技术
配置和管理Nginx服务
配置和管理Nginx服务
516 4

热门文章

最新文章