接着上一篇的对象结构探索,我们详细介绍cache_t。源码为最新的objc4-818.2。
cache_t的底层结构
struct cache_t {
// 省略一堆私有属性,方法
public:
// The following four fields are public for objcdt's use only.
// objcdt reaches into fields while the process is suspended
// hence doesn't care for locks and pesky little details like this
// and can safely use these.
unsigned capacity() const;
struct bucket_t *buckets() const;
Class cls() const;
mask_t occupied() const;
void initializeToEmpty();
void insert(SEL sel, IMP imp, id receiver);
void copyCacheNolock(objc_imp_cache_entry *buffer, int len);
void destroy();
void eraseNolock(const char *func);
};
mask_t cache_t::occupied() const
{
return _occupied;
}
unsigned cache_t::capacity() const
{
return mask() ? mask()+1 : 0;
}
Class cache_t::cls() const
{
return (Class)((uintptr_t)this - offsetof(objc_class, cache));
}
struct bucket_t *cache_t::buckets() const
{
uintptr_t addr = _bucketsAndMaybeMask.load(memory_order_relaxed);
return (bucket_t *)(addr & bucketsMask);
}
核心逻辑就在增加方法的时候,现在是insert方法。
void cache_t::insert(SEL sel, IMP imp, id receiver)
{
// Use the cache as-is if until we exceed our expected fill ratio.
// 获取实际容量再加1
mask_t newOccupied = occupied() + 1;
// 获取总容量
unsigned oldCapacity = capacity(), capacity = oldCapacity;
if (slowpath(isConstantEmptyCache())) {// 如果为空,那么创建一个初始化的cache,
// Cache is read-only. Replace it.
// INIT_CACHE_SIZE为4
if (!capacity) capacity = INIT_CACHE_SIZE;
reallocate(oldCapacity, capacity, /* freeOld */false);
}
else if (fastpath(newOccupied + CACHE_END_MARKER <= cache_fill_ratio(capacity))) {
// Cache is less than 3/4 or 7/8 full. Use it as-is.
}
// 这是最新的代码,允许缓存总大小比较小的时候 可以百分百利用,不去创建多余的空间
// FULL_UTILIZATION_CACHE_SIZE 为8 arm64 CACHE_END_MARKER为0 也就是小于8的可以占满空间
else if (capacity <= FULL_UTILIZATION_CACHE_SIZE && newOccupied + CACHE_END_MARKER <= capacity) {
// Allow 100% cache utilization for small buckets. Use it as-is.
}
else {
// 最后进行扩容
capacity = capacity ? capacity * 2 : INIT_CACHE_SIZE;
if (capacity > MAX_CACHE_SIZE) {
capacity = MAX_CACHE_SIZE;
}
reallocate(oldCapacity, capacity, true);
}
bucket_t *b = buckets();
mask_t m = capacity - 1;
mask_t begin = cache_hash(sel, m);
mask_t i = begin;
// Scan for the first unused slot and insert there.
// There is guaranteed to be an empty slot.
do {// 清空sel为空的缓存
if (fastpath(b[i].sel() == 0)) {
incrementOccupied();
b[i].set<Atomic, Encoded>(b, sel, imp, cls());
return;
}
if (b[i].sel() == sel) {
// The entry was added to the cache by some other thread
// before we grabbed the cacheUpdateLock.
return;
}
} while (fastpath((i = cache_next(i, m)) != begin));
bad_cache(receiver, (SEL)sel);
#endif // !DEBUG_TASK_THREADS
}
void cache_t::reallocate(mask_t oldCapacity, mask_t newCapacity, bool freeOld)
{
bucket_t *oldBuckets = buckets();
bucket_t *newBuckets = allocateBuckets(newCapacity);
setBucketsAndMask(newBuckets, newCapacity - 1);
if (freeOld) {
collect_free(oldBuckets, oldCapacity);
}
}
- 首先判断曾经是否是空缓存,再判断是否曾经创建过,没有创建过的初始化大小为4的容量
- 如果新增后,大小小于四分之三,或者8分之7,直接增加
- 反之,扩容,扩容为两倍大小,如果愿容量为0,则扩容为初始化大小4,如果扩容后的大小超过最大的容量1左移16位就是65536,则容量为最大容量65536
- 插入新的方法到buckets内,调用bucket_t内的set方法,存储在私有变量_sel数组和_imp数组中
-
reallocate每次调用完都会清空之前的缓存
-
static inline mask_t cache_hash(SEL sel, mask_t mask) { uintptr_t value = (uintptr_t)sel; return (mask_t)(value & mask); }
bucket_t的结构为
struct bucket_t { private: explicit_atomic<uintptr_t> _imp; explicit_atomic<SEL> _sel; public: template <Atomicity, IMPEncoding> void set(bucket_t *base, SEL newSel, IMP newImp, Class cls); } template<Atomicity atomicity, IMPEncoding impEncoding> void bucket_t::set(bucket_t *base, SEL newSel, IMP newImp, Class cls) { // objc_msgSend uses sel and imp with no locks. // It is safe for objc_msgSend to see new imp but NULL sel // (It will get a cache miss but not dispatch to the wrong place.) // It is unsafe for objc_msgSend to see old imp and new sel. // Therefore we write new imp, wait a lot, then write new sel. uintptr_t newIMP = (impEncoding == Encoded ? encodeImp(base, newImp, newSel, cls) : (uintptr_t)newImp); if (atomicity == Atomic) { _imp.store(newIMP, memory_order_relaxed); if (_sel.load(memory_order_relaxed) != newSel) { mega_barrier(); _sel.store(newSel, memory_order_relaxed); } } else { _imp.store(newIMP, memory_order_relaxed); _sel.store(newSel, memory_order_relaxed); } }