1、Class的结构
/*
struct objc_class {
Class isa;
Class superclass;
cache_t cache; //方法缓存
class_data_bits_t bits; // 用于获取具体的类信息 (位域技术---&FAST_DATA_MASK---> struct class_rw_t)
};
struct class_rw_t {
uint32_t flags;
uint32_t version;
const class_ro_t *ro; // (指向------> struct class_ro_t)
method_list_t *methods; // 方法列表
property_list_t *properties; // 属性列表
const protocol_list_t *protocols; // 协议列表
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
};
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize; // instance 对象占用的空间
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t *ivarLayout;
const char *name; // 类名
method_list_t *baseMethodList;
protocol_list_t *baseProtocols;
const ivar_list_t * ivars; // 成员变量列表
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
}
*/
- class_rw_t里面的methods、properties、protocols实际上是二维数组,是可读可写的,包含了类的初始内容、分类的内容
- class_ro_t里面的baseMethodList、baseProtocols、ivars、baseProperties是一维数组,是只读的,包含了类的初始内容
2、Type Encoding
- iOS中,提供了一个叫@encode的指令,可以将具体的类型表示成字符串编码
NSLog(@“SEL->%s\nvoid->%s”,@encode(SEL),@encode(void));
打印结果:
SEL->:
void->v
3、方法缓存
struct objc_class {
Class isa;
Class superclass;
cache_t cache; //方法缓存
class_data_bits_t bits; // 用于获取具体的类信息 (位域技术---&FAST_DATA_MASK---> struct class_rw_t)
};
struct class_rw_t {
uint32_t flags;
uint32_t version;
const class_ro_t *ro; // (指向------> struct class_ro_t)
method_list_t *methods; // 方法列表
property_list_t *properties; // 属性列表
const protocol_list_t *protocols; // 协议列表
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
};
第一次会到 <method_list_t *methods; // 方法列表> 里面去寻找
如果这里面没找到,会到父类里面取寻找
如果父类没寻找到,会到基类里面寻找
其中只要一旦找到了,就会放在<cache_t cache; //方法缓存> 里面
下次再调用这个方法,就不用再很麻烦地去寻找这个方法了
struct cache_t {
struct bucket_t *_buckets; // 散列表
mask_t _mask; // 散列表的长度 -1
mask_t _occupied; // 已经缓存的方法数量
}
struct bucket_t {
cache_key_t _key; // SEL 作为key
IMP _imp; // 函数的内存地址
}
// 调用方法之前,先来这个缓存里面查看,是否有过缓存,如果有,就直接通过缓存找到函数地址,去调用函数,这样效率就会很高了
4、散列缓存的原理
- 下面这个图应该能表达清楚了