- Class内部结构中有个方法缓存(cache_t),用
散列表(哈希表)
来缓存曾经调用过的方法,可以提高方法的查找速度。
struct cache_t {
struct bucket_t *_buckets; //散列表 (bucket_t, ...)
mask_t _mask; //散列表的长度 - 1
mask_t _occupied; //已经缓存的方法数量
};
|
|
struct bucket_t {
cache_key_t _key; // SEL作为Key
IMP _imp; //函数的内存地址
};
- 缓存查找:
bucket_t * cache_t::find(cache_key_t k, id receiver)
散列表(哈希表)
散列表找元素的原理:
f(key) = index
- 通过一个函数,传入一个
key
,算出一个索引index
。 - 如果这个索引冲突了,那就通过
+ 1
、- 1
或某个算法再算一遍,直到不冲突为止。
应用
- 在存储
bucket_t
时,将这个_key&_mask
得到一个值,方法就存在相应的位置; - 取方法时,也是直接
_key&_mask
,去对应的值里取bucket_t
;
static inline mask_t cache_hash(cache_key_t key, mask_t mask)
{
return (mask_t)(key & mask);
}
- 由图表看到好多空间没有使用到,故,这是一种牺牲空间使用效率,来换取查询效率的方法。
如_key&_mask
的结果和之前的重复了怎么办 ?
即: _key&_mask
之后,该位置上已经有bucket_t
了或者取出来的bucket_t
的_key
对不上。
解决:
以存为例:
- 得出的值,位置有
bucket_t
; - 值
- 1
,如果还有,继续-1
; - 值 =
0
,就直接等于mask
(mask == 散列表的长度 - 1
)。
static inline mask_t cache_next(mask_t i, mask_t mask) {
return i ? i-1 : mask;
}
如果方法过多,散列表原来的长度不够了怎么办?
- 扩容为原来空间的2倍;
- 将原缓存清空。
void cache_t::expand()
{
cacheUpdateLock.assertLocked();
uint32_t oldCapacity = capacity();
uint32_t newCapacity = oldCapacity ? oldCapacity*2 : INIT_CACHE_SIZE; // 2倍
.........
reallocate(oldCapacity, newCapacity); //里面有个" cache_collect_free(oldBuckets, oldCapacity);"
}
网友评论