object_class
继承:objc_object
内部:
Class superClass; 父类
cache_t cache; 缓存
class_data_bits_t bits 数据
isa指针关系
实例对象其指针指向类对象
类对象其指针指向元类对象
cache_t
https://www.jianshu.com/p/45d409e77806
用散列表(哈希表)来缓存曾经调用过的方法,可以提高方法的查找速度,底层结构如下:
用于快速查找方法执行函数
是可增量扩展的哈希表结构
是局部性原理的最佳应用
cache 本质是一个 Hash表。存储bucket_t
bucket_t存储
cache_key_t _key;//@selector()
IMP _imp;//函数地址
哈希查找:发现当发生碰撞的时候,索引会+1,查找下一个。
槽位如果不够,_mask 会变换,变为原来的2倍,并且扩展槽位的时候,会清空数组里原有的缓存内容
子类没有实现方法会调用父类的方法,会将父类方法加入到子类自己的cache 里。
为什么要创建新的新的buckets来替换原有的buckets并抹掉原有的buckets的方案,而不是在在原有buckets的基础上进行扩容?
减少对方法快速查找流程的影响:调用objc_msgSend时会触发方法快速查找,如果进行扩容需要做一些读写操作,对快速查找影响比较大。
对性能要求比较高:开辟新的buckets空间并抹掉原有buckets的消耗比在原有buckets上进行扩展更加高效
当扩容后,会把新mask设置为newCapacity长度减一,然后清空缓存。
所有散列表(哈希表)的原理都是一样的,都是通过一个函数,这个函数传入一个key,通过这个key算出一个索引,如果索引冲突了就加一或者减一,直至不冲突为止,不同的就是算法不一样。
struct class_rw_t {
uint32_t flags;
uint32_t version;
const class_ro_t *ro;
method_array_t methods;
property_array_t properties; //属性,和变量的区别,属性是名称,变量是值
protocol_array_t protocols;
Class firstSubclass;
Class nextSiblingClass;
};
class_rw_t结构体内有一个指向class_ro_t结构体的指针。
细看两个结构体的成员变量会发现很多相同的地方,他们都存放着当前类的属性、实例变量、方法、协议等等。区别在于:class_ro_t存放的是编译期间就确定的;而class_rw_t是在runtime时才确定,它会先将class_ro_t的内容拷贝过去,然后再将当前类的分类的这些属性、方法等拷贝到其中。所以可以说class_rw_t是class_ro_t的超集,当然实际访问类的方法、属性等也都是访问的class_rw_t中的内容
哈希碰撞
开放地址法
链地址法
struct method_t {
SEL name; //函数名
const char *types; //编码(返回值类型、参数类型)
IMP imp;//指向函数的指针(函数地址)
};
instance对象的isa指向class对象
class对象的isa指向meta-class对象
meta-class对象的isa指向基类的meta-class对象
发消息的步骤
检查target是不是nil ObjC的特性允许对一个nil对象发消息而不会崩溃
通过isa,
对象方法查找类对象(类对象及继承链上找方法)
方法缓存列表,hashMap的结构,查找速度快
方法缓存找不到就查找类对象的方法列表中遍历class_rw_t,从后向前的遍历
查找父类的类对象—-同上,直至nsobjcect
类方法查找元类对象(元类对象及继承链上找方法)
方法缓存列表,hashMap的结构,查找速度快
方法缓存找不到就查找元类对象的方法列表中遍历class_rw_t,从后向前的遍历