objc4 Selector / SEL 注册、唯一化与方法名查找
Selector / SEL 注册、唯一化与方法名查找 在 objc4 中,SEL 的关键价值不是承载一段复杂对象,而是把方法名字符串 “驻留”为全进程唯一的指针。注册、镜像加载 fixup、方法列表排序、方法查找和消息缓存都建立在这个事实上: 同名 selector 最终拥有同一个地址,因此比较 selector 可以退化为一次指针比较。 作用 统一方法名身份 编译器、Mach-O 镜像、动态注册 API 和运行时内部方法列表都可能产生方法名。selector 唯一化后,同一个方法名只对应一个 SEL。 让查找足够便宜 方法列表和缓存查找不需要反复比较字符串。命中条件基本都是 method.name == sel 或 bucket.sel == _cmd。 支撑 dyld 预优化 共享缓存中的 selector 可以由 dyld 提供内建唯一值;未优化镜像在 _read_images 中被 runtime 修正到唯一值。 对外头文件把 SEL 声明为不透明类型:typedef struct objc_selector *SEL;。 但在当前实现中,runtime 会把 selector 当成方法名 C 字符串指针使用,sel_getName() 直接把 SEL 转回 const char *。 核心结构、函数与字段 SEL 公开为不透明 selector 指针,实际唯一值是某个方法名字符串的地址。来源:runtime/objc.h。 namedSelectors ExplicitInitDenseSet<const char *>,保存非 dyld 内建 selector 的唯一字符串指针。来源:runtime/objc-sel.mm。 selLock 保护 selector 表。sel_registerName() 自行加锁;镜像加载和方法列表 fixup 常在外层持锁后调用 sel_registerNameNoLock()。 _sel_searchBuiltins() 在支持预优化时向 dyld 查询共享缓存内建 selector,优先返回 dyld 已唯一化的值。 sel_registerName() 注册并返回 selector;若已存在则返回既有唯一值;会复制传入字符串。 sel_lookUpByName() 只查找,不创建。未注册返回 NULL,测试 test/sel.m 明确覆盖该行为。 method_t::name 方法条目的 selector 字段。方法列表 fixup 后,这个字段应是唯一化的 SEL。 cache_t / bucket_t 消息缓存以 SEL 为 key,桶保存 SEL + IMP,hash 直接来自 selector 地址。 实现原理 selector interning 的核心是“先按名字查找唯一槽,再把槽里的字符串地址作为 SEL 返回”。 对 dyld 共享缓存内建 selector,runtime 先问 dyld;对普通 selector,runtime 使用 namedSelectors 保存唯一字符串。 ...