isa详解
在arm64架构之前,isa就是一个普通的指针,存储着Class、Meta-Class对象的内存地址
从arm64架构开始,对isa进行了优化,变成了一个共用体(union)结构,还使用位域来存储更多的信息
nonpointer
0,代表普通的指针,存储着Class、Meta-Class对象的内存地址
1,代表优化过,使用位域存储更多的信息
has_assoc
是否有设置过关联对象,如果没有,释放时会更快
has_cxx_dtor
是否有C++的析构函数(.cxx_destruct),如果没有,释放时会更快
shiftcls
存储着Class、Meta-Class对象的内存地址信息
magic
用于在调试时分辨对象是否未完成初始化
weakly_referenced
是否有被弱引用指向过,如果没有,释放时会更快
deallocating
对象是否正在释放
extra_rc
里面存储的值是引用计数器减1
has_sidetable_rc
引用计数器是否过大无法存储在isa中 如果为1,那么引用计数会存储在一个叫SideTable的类的属性中
所以我们在源码中看到类对象,元类对象的实际地址值都要用isa地址& ISA_MASK 一个值,这就是位运算来获得实际shiftcls值。
# define ISA_MASK 0x00007ffffffffff8ULL
由isa我们重新来看class结构
class_rw_t
由class_data_bits_t & MASK值获得class_rw_t,
class_rw_t里面的methods、properties、protocols是二维数组,是可读可写的,包含了类的初始内容、分类的内容
class_ro_t
class_ro_t里面的baseMethodList、baseProtocols、ivars、baseProperties是一维数组,是只读的,包含了类的初始内容
method_t
method_t是对方法\函数的封装
SEL代表方法\函数名,一般叫做选择器,底层结构跟char *类似
- 可以通过@selector()和sel_registerName()获得
- 可以通过sel_getName()和NSStringFromSelector()转成字符串
- 不同类中相同名字的方法,所对应的方法选择器是相同的
types包含了函数返回值、参数编码的字符串
一般我们正常写的函数都默认包含了两个参数(self,SEL),我们写申明一个test方法,
- (void)test;
- (void)test{
}
打印出来types就变成 aaa@qq.com:8,v代表返回值,16为总的字节,@代表self,0表示从第0哥开始,:代表SEL
cache
Class内部结构中有个方法缓存(cache_t),用散列表(哈希表)来缓存曾经调用过的方法,可以提高方法的查找速度
缓存原理为地址值& _mask 为索引,SEL作为key,用空间换时间,当这个索引存在时,就会到索引减1那,还有值时,继续减1,为0时 则扩充_mask 并清空之前的数据,因为&_mask 得到的值肯定会小于等于_mask,这样就不会越界。
bucket_t * cache_t::find(SEL s, id receiver)
{
assert(s != 0);
bucket_t *b = buckets();
mask_t m = mask();
mask_t begin = cache_hash(s, m);
mask_t i = begin;
do {
if (b[i].sel() == 0 || b[i].sel() == s) {
return &b[i];
}
} while ((i = cache_next(i, m)) != begin);
// hack
Class cls = (Class)((uintptr_t)this - offsetof(objc_class, cache));
cache_t::bad_cache(receiver, (SEL)s, cls);
}
static inline mask_t cache_hash(SEL sel, mask_t mask)
{
return (mask_t)(uintptr_t)sel & mask;
}
上一篇: ret指令 和 retf指令
下一篇: 如何创建一个简单的电话号码选择器
推荐阅读