iOS 中类的结构
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits;
}
从上述代码中我们发现了一个 cache_t 的结构体 下边来看cache_t的源码
cache_t 缓存探索
struct cache_t {
struct bucket_t *_buckets;
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(cache_key_t key, id receiver);
static void bad_cache(id receiver, SEL sel, Class isa) __attribute__((noreturn));
};
从这个源码中我们发现了几个方法
- occupied() 占位
- capacity() 容纳能力 联想到NSMutableArray的capacity
- setBucketsAndMask 设置桶 和mask
- expand() 扩容
- void reallocate(mask_t oldCapacity, mask_t newCapacity); 真正的开辟空间
那么我们随便找一个方法 看他在哪里调用 非常幸运的让我找到了下边的代码
static void cache_fill_nolock(Class cls, SEL sel, IMP imp, id receiver)
{
cacheUpdateLock.assertLocked();
// Never cache before +initialize is done
if (!cls->isInitialized()) return;
// Make sure the entry wasn't added to the cache by some other thread
// before we grabbed the cacheUpdateLock.
if (cache_getImp(cls, sel)) return;
cache_t *cache = getCache(cls);
cache_key_t key = getKey(sel);
// Use the cache as-is if it is less than 3/4 full
mask_t newOccupied = cache->occupied() + 1;
mask_t capacity = cache->capacity();
if (cache->isConstantEmptyCache()) {
// Cache is read-only. Replace it.
cache->reallocate(capacity, capacity ?: INIT_CACHE_SIZE);
}
else if (newOccupied <= capacity / 4 * 3) {
// Cache is less than 3/4 full. Use it as-is.
}
else {
// Cache is too full. Expand it.
cache->expand();
}
// Scan for the first unused slot and insert there.
// There is guaranteed to be an empty slot because the
// minimum size is 4 and we resized at 3/4 full.
bucket_t *bucket = cache->find(key, receiver);
if (bucket->key() == 0) cache->incrementOccupied();
bucket->set(key, imp);
}
ok 现在让我们一行一行的分析这个流程
- if (!cls->isInitialized()) return; 初始化过程没结束 不缓存
- if (cache_getImp(cls, sel)) return; 拿到缓存方法直接返回
- cache_key_t key = getKey(sel); 拿到sel 作为键
- mask_t newOccupied = cache->occupied() + 1; 初始化一个占位第一次是0 这个变量只是用来判断
- mask_t capacity = cache->capacity(); 获取当前的容量
- 这一步很关键 如果缓存是空直接创建 如果 当前占位+1后超过原来空间的3/4 就扩容 INIT_CACHE_SIZE 为 = (1 << INIT_CACHE_SIZE_LOG2) INIT_CACHE_SIZE_LOG2 = 2 1左移两位 变成4 首次开辟空间 4
if (cache->isConstantEmptyCache()) {
// Cache is read-only. Replace it.
cache->reallocate(capacity, capacity ?: INIT_CACHE_SIZE);
}
else if (newOccupied <= capacity / 4 * 3) {
// Cache is less than 3/4 full. Use it as-is.
}
else {
// Cache is too full. Expand it.
cache->expand();
}
- 来看reallocate 应该是为了性能考虑 这里直接废弃老桶 创建新桶
void cache_t::reallocate(mask_t oldCapacity, mask_t newCapacity)
{
bool freeOld = canBeFreed();
bucket_t *oldBuckets = buckets();
bucket_t *newBuckets = allocateBuckets(newCapacity);
// Cache's old contents are not propagated.
// This is thought to save cache memory at the cost of extra cache fills.
// fixme re-measure this
assert(newCapacity > 0);
assert((uintptr_t)(mask_t)(newCapacity-1) == newCapacity-1);
setBucketsAndMask(newBuckets, newCapacity - 1);
if (freeOld) {
cache_collect_free(oldBuckets, oldCapacity);
cache_collect(false);
}
}
- 再来看扩容 cache->expand(); 如果有oldCapacity 就宽展成原来的2倍 最后走reallocate
void cache_t::expand()
{
cacheUpdateLock.assertLocked();
uint32_t oldCapacity = capacity();
uint32_t newCapacity = oldCapacity ? oldCapacity*2 : INIT_CACHE_SIZE;
if ((uint32_t)(mask_t)newCapacity != newCapacity) {
// mask overflow - can't grow further
// fixme this wastes one bit of mask
newCapacity = oldCapacity;
}
reallocate(oldCapacity, newCapacity);
}
- 最后来看设置的部分 incrementOccupied 会将_occupied ++ 正好也对应到了用于判断是否超过3/4容量的部分
bucket_t *bucket = cache->find(key, receiver);
if (bucket->key() == 0) cache->incrementOccupied();
bucket->set(key, imp);
set 就很简单了 就是给 key 和 imp 赋值
void bucket_t::set(cache_key_t newKey, IMP newImp)
{
assert(_key == 0 || _key == newKey);
// objc_msgSend uses key and imp with no locks.
// It is safe for objc_msgSend to see new imp but NULL key
// (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 key.
// Therefore we write new imp, wait a lot, then write new key.
_imp = newImp;
if (_key != newKey) {
mega_barrier();
_key = newKey;
}
}