1. cache_t & bucket_t 结构
struct cache_t {
struct bucket_t *_buckets; // 桶结构[结构体],保存缓存 method_t 列表的数据结构
mask_t _mask; // 当前缓存桶结构的大小
mask_t _occupied; // 当前缓存方法数量
// ------- 方法 ----------
public:
struct bucket_t *buckets();
mask_t mask();
mask_t occupied();
void incrementOccupied();
void setBucketsAndMask(struct bucket_t *newBuckets, mask_t newMask);
void initializeToEmpty();
mask_t capacity();
bool isConstantEmptyCache();
bool canBeFreed();
static size_t bytesForCapacity(uint32_t cap);
static struct bucket_t * endMarker(struct bucket_t *b, uint32_t cap);
void expand();
void reallocate(mask_t oldCapacity, mask_t newCapacity);
struct bucket_t * find(SEL sel, id receiver);
static void bad_cache(id receiver, SEL sel, Class isa) __attribute__((noreturn));
};
struct bucket_t {
private:
// IMP-first is better for arm64e ptrauth and no worse for arm64.
// SEL-first is better for armv7* and i386 and x86_64.
#if __arm64__
uintptr_t _imp; // 方法 IMP
SEL _sel; // 方法 SEL 对应 key
#else
SEL _sel;
uintptr_t _imp;
#endif
};
2. 查找方法缓存时机
在调用 objc_msgSend 方法的之前会调用 getCache 方法从 cache_t 中的缓存 bucket 列表中查找,如果存在缓存直接返回缓存 IMP 实现,否则走正常的方法查找流程。
3. 缓存组合结构
- 缓存方法存储桶结构 bucket_t
- 缓存结构容量 capacity = mask + 1
- 当前占用容量 occupied
4. 缓存策略
- 默认容量 capacity = 4
- 当占用超过容量的 3/4 时需要扩容为当前容量的两倍, 采用3/4的节点是为了提前扩容,保证容量够存储,否则到了临界点扩容容易溢出或者造成其他问题
- 重新申请容量为两倍的缓存结构内存,释放掉之前的内存,然后缓存当前正在调用的方法
- 方法存储通过 cache_hash 方法哈希 SEL & mask 得到的哈希值对应方法,find 查找过程也是通过该哈希值key,采用开放寻址算法快速查找