cache_t 实例内存(arm64 真机,共 16 bytes)
offset 0 · 8 bytes
_bucketsAndMaybeMask
uintptr_t · 位域打包
offset 8 · 8 bytes
union { struct | preoptCache }
两套身份共享内存
字段 1 — _bucketsAndMaybeMask
explicit_atomic<uintptr_t>
_bucketsAndMaybeMask;
一个整数,同时打包了 mask(高16位)和 buckets 指针(低44位)
字段 2 — union
union {
struct {
uint32_t _disguised…;
uint16_t _occupied;
uint16_t _flags;
};
preopt_cache_t *_orig…;
};
static constexpr 常量块
不占实例内存,编译期内联。是 _bucketsAndMaybeMask 的"解码规则",定义 maskShift / maskZeroBits / bucketsMask 等。
_bucketsAndMaybeMask 64位位域(arm64 HIGH_16)
buckets pointer
bit 43..0(44位)
bit 63bit 0
常量定义
常量值含义
maskShift
48
mask 起始位
maskZeroBits
4
强制零位,优化用
maxMask
0xFFFF
最大容量 65535
bucketsMask
0x00000FFFFFFFFFFF
低44位掩码
解码方法(源码节选)
buckets_t *buckets() {
return (buckets_t *)
(_bucketsAndMaybeMask & bucketsMask);
}
mask_t mask() {
return (mask_t)
(_bucketsAndMaybeMask >> maskShift);
}
// msgSend 单指令得到 mask<<4:
// LSR x1, x0, #44 (44 = maskShift - maskZeroBits)
maskZeroBits=4 的作用:mask 存放在 bit 63..48,紧邻的 4 位为零,相当于 mask 已预先左移 4 位。msgSend 只需 LSR #44 就能同时完成"提取 mask"和"× 16",省一条指令。
union 8 bytes(offset 8),两个成员完全重叠
普通缓存状态
uint32_t
_disguisedPreopt…; // 占位
uint16_t _occupied; // 已填槽数
uint16_t _flags; // 快速标志位
_disguisedPreoptCacheSignature 是纯占位符,把 _flags 推到 offset 6
preopt 缓存状态
explicit_atomic<
preopt_cache_t *,
PTRAUTH_STR(…)
> _originalPreoptCache;
dyld 预生成的方法哈希表指针,带 PAC 签名
内存偏移对齐约束
offset 0–3
_disguisedPreopt…
uint32,纯占位
offset 4–5
_occupied
uint16
_flags 落在 offset 6,对应 _originalPreoptCache 指针的 bit 63..48(符号扩展位)。null 指针时这些位全零,与标志位"0=默认实现"的语义天然吻合。
汇编硬编码警告
objc_release_xN 汇编直接用立即数偏移读取 _flags 里的 FAST_CACHE_HAS_CUSTOM_DEALLOC_INITIATION。修改字段顺序必须同步修改 .s 文件。
bucket_t — 方法缓存哈希表的单个槽位(arm64,16 bytes)
offset 0 · 8 bytes
_imp
uintptr_t · 编码后的 IMP
offset 8 · 8 bytes
_sel
SEL · 方法选择子
IMP 编码策略(三路)
策略源码
bitcast_auth_and_resign(
void *, newImp,
ptrauth_key_function_pointer, …,
ptrauth_key_process_dependent_code,
modifierForSEL(base, newSel, cls)
);
return (uintptr_t)newImp ^ (uintptr_t)cls;
// 还原:imp ^ (uintptr_t)cls
return (uintptr_t)newImp;
modifierForSEL — ptrauth discriminator
uintptr_t modifierForSEL(
bucket_t *base, SEL newSel, Class cls) {
return (uintptr_t)base // 绑定缓存数组
^ (uintptr_t)newSel // 绑定 SEL
^ (uintptr_t)cls; // 绑定类
}
objc_msgSend 缓存查找全链路
① 读取 _bucketsAndMaybeMask
uintptr_t val = _bucketsAndMaybeMask;
mask_t mask = val >> 48; // 高16位
buckets_t *buckets = val & bucketsMask; // 低44位
② 哈希定位 bucket
index = (uintptr_t)sel & mask; // 位与代替取模(capacity = 2^n)
bucket_t *b = buckets + index;
③ 线性探测比较 _sel
if (b->_sel == sel) goto hit; // 命中
b--; // 向前探测(环形)
if (b < buckets) b = &buckets[mask];
if (b->_sel == 0) goto miss; // 空槽,未命中
命中 → 解码 IMP
IMP fn = b->imp(base, cls);
// PTRAUTH: AUTIB 验签
// ISA_XOR: imp ^ cls
jmp fn;
未命中 → 慢速路径
lookUpImpOrForward()
→ 遍历方法列表
→ 写入缓存
→ 执行
完整数据链路
cache_t(16 bytes,内嵌在 objc_class)
└─ _bucketsAndMaybeMask
├─ [63..48] mask → capacity = mask + 1
└─ [43.. 0] ptr → bucket_t[](堆,动态分配)
├─ [0] { _imp(编码), _sel }
├─ [1] { _imp(编码), _sel }
└─ [mask] { _imp(编码), _sel }
每个 bucket_t 16 bytes:_imp(8) + _sel(8)