cache_t 分析

156 阅读2分钟

浅析cache_t

一个类的基本参数
struct objc_class : objc_object {
Class ISA;
Class superclass; 
cache_t cache; 
class_data_bits_t bits; // class_rw_t 
}

cache_t cache;看名称我们知道是缓存,它的用途是用来缓存方法的,cache_t本身是缓存bucket

这是它的基本构造,一眼可以看出他是个机构体,

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));
};

那么里面struct bucket_t *_buckets;  mask_t _mask; mask_t _occupied;  这三个到底是干嘛的,我们分别进行探索

_buckets缓存验证

struct bucket_t *_buckets 这个东西我们看下它的内部构造

MethodCacheIMP _imp;
cache_key_t _key

imp 是不是很熟悉,验证cache_t缓存的是方法, 绑定方法的指针和具体实现之间的联系


这里补充一下方法的知识点:

SEL name;
const char *types;
MethodListIMP imp;

这里 SEL 和 imp的关系基本就是书本里面目录 页码和内容的关系,通过SEL 寻找imp,来查找方法的具体实现


我们用实际代码来,我们来验证下cache_t里面确实缓存的是方法

我们定义LGPerson这样一个类


在不调用任何方法前提下打印cache_t 和 buckets


我们通过上图观察断点位置,可以看到在不调用任何方法的情况下 cache_t里面的buckets里面什么都没有

下面我们来调用下sayHello的方法,打印cache_t和buckets做验证


看上图我们了解sayHello这个方法的确是缓存在了cache_t里面,


_mask缓存流程

我们由上图得知_buckets发生改变的同时,_mask也会发生相应的增长.它本身的作用就是开辟当前缓存空间的大小,同时我对mask_t mask() 该函数的一个追踪在大神的指导下,整理了这么缓存一个流程图,补充:mask_t _occupied; 这个是已占用的容量大小



最后补充

这里的方法,系统也并不是说会来一个方法,就会缓存一个,系统也会对其作出相应处理

系统在扩容的时候,原先容器里的数据不是直接拷贝,而是直接清除,下次再进行写入 ,原因有二:1不安全 ,2本身操作速度比较快,下次直接写入更加便捷 .