SEL、IMP、cache、“v@:”、_cmd
如果说 objc_msgSend 是 Objective-C Runtime 的发动机,
那么下面这五个概念,就是发动机里最关键的零部件。
本文将围绕它们,回答三个核心问题:
- 它到底是什么?
- Runtime 在什么时候、为什么要用它?
一、SEL —— 消息的名字,而不是方法
1. SEL 是什么
typedef struct objc_selector *SEL;
- SEL 不是字符串
- 是一个 全局唯一的 Selector 标识
- 相同的方法名,在进程内只有一个 SEL
@selector(foo) == sel_registerName("foo"); // YES
SEL 只表示“要发什么消息”,不表示“怎么实现”。
2. SEL 在 Runtime 中的作用
在消息发送时:
objc_msgSend(obj, sel);
Runtime 使用 SEL:
-
在 cache 中查找 IMP
-
在 method list 中匹配方法
-
在转发阶段判断消息类型
整个 Runtime 流程,都是围绕 SEL 的查找与匹配 展开的。
二、IMP —— 真正被执行的函数
1. IMP 是什么
typedef id (*IMP)(id self, SEL _cmd, ...);
-
IMP 本质是一个 C 函数指针
-
指向方法的真实实现地址
-
找到 IMP,Runtime 的任务就结束了
Objective-C 的“动态”,到 IMP 为止。
2. IMP 在 Runtime 中的作用
当 cache 命中时:
imp(self, _cmd);
此时:
-
不再有方法查找
-
不再有消息机制
-
只剩一次普通的 C 函数调用
这也是为什么:
cache 命中后,方法调用速度接近 C 函数。
三、cache —— 方法调用的高速缓存
1. cache 是什么
- cache 是一个 SEL → IMP 的哈希表
- 位于 Class 结构体中
- 用于加速方法调用
Class
├─ cache
├─ method list
└─ superclass
2. cache 在 Runtime 中的作用
objc_msgSend 的查找顺序:
cache → method list → superclass
-
绝大多数方法调用只走 cache
-
method list 和 superclass 查找是慢路径
cache 是 Runtime 性能的生命线。
四、“v@:” —— 方法类型编码(Type Encoding)
1. “v@:” 是什么
v 返回值 void
@ self(id)
: _cmd(SEL)
表示方法签名:
- (void)foo;
等价于 C 函数:
void foo(id self, SEL _cmd);
2. Type Encoding 在 Runtime 中的作用
-
描述 IMP 的真实参数布局
-
NSInvocation / 消息转发严重依赖它
-
Runtime 不会自动校验是否匹配
Runtime 相信你是对的。
五、_cmd —— 当前正在执行的方法名
1. _cmd 是什么
- _cmd 是一个隐藏参数
- 表示当前方法的 SEL
- (void)foo {
_cmd == @selector(foo); // YES
}
2. _cmd 在 Runtime 中的作用
- Runtime 自动传入
- 用于日志、AOP、转发识别
NSLog(@"当前方法:%@", NSStringFromSelector(_cmd));
六、五者之间的关系(一定要理解)
SEL —— 决定“调用哪个方法”
cache—— 决定“能否快速找到”
IMP —— 决定“执行哪段代码”
"v@:" —— 决定“如何正确调用”
_cmd —— 决定“当前正在执行谁”
它们共同组成了 Objective-C 的方法调用体系。
七、总结一句话版本
SEL 是钥匙,IMP 是门,cache 是高速路,
“v@:” 是地图,_cmd 是门牌号。
理解它们,
你就真正站在了 Objective-C Runtime 的入口处。