在 32 位环境下,对象的引用计数都保存在一个外部的表中,每一个对象的 Retain 操作,实际包括如下 5 个步骤:

  1. 获得全局的记录引用计数的 hash 表;
  2. 为了线程安全,给该 hash 表加锁;
  3. 查找到目标对象的引用计数值;
  4. 将该引用计数值加 1,写回 hash 表;
  5. 给该 hash 表解锁。

而在 64 位环境下,isa 指针也是 64 位,实际作为指针部分只用到其中 33 位,剩余的 31 位苹果使用了类似 Tagged Pointer 的概念,其中 19 位将保存对象的引用计数,这样对引用计数的操作只需要修改这个指针即可。只有当引用计数超出 19 位,才会将引用计数保存到外部表,但是这种情况是很少的。在 64 位环境下,新的 Retain 操作包括如下 5 个步骤:

  1. 检查 isa 指针上面的标记位,看引用计数是否保存在 isa 变量中,如果不是,则使用以前的步骤,否则执行第 2 步;
  2. 检查当前对象是否正在释放,如果是,不做任何事情;
  3. 增加该对象的引用计数,但是并不马上写回到 isa 变量中;
  4. 检查增加后的引用计数的值是否能够被 19 位表示,如果不是,则切换成以前的办法,否则执行第 5 步;
  5. 进行一个原子的写操作,将 isa 的值写回。

虽然步骤都是 5 步,但是由于没有了全局的加锁操作,所以引用计数的更改更快了。

10.5 isa 的 bit 位含义

bit 位

变量名

意义

1 bit

indexed

0 表示普通的 isa,1 表示 Tagged Pointer

1 bit

has_assoc

表示该对象是否有过 associated 对象,如果没有,在析构释放内存时可以更快

1 bit

has_cxx_dtor

表示该对象是否有 C++ 或 ARC 的析构函数,如果没有,在析构释放内存时可以更快

30 bits

shiftcls

类的指针

9 bits

magic

其值固定为 0xd2,用于在调试时分辨对象是否未完成初始化

1 bit

weakly_referenced

表示该对象是否有过 weak 对象,如果没有,在析构释放内存时可以更快

1 bit

deallocating

表示该对象是否正在析构

1 bit

has_sidetable_rc

表示该对象的引用计数值是否大到无法直接在 isa 中保存

19 bits

extra_rc

表示该对象超过 1 的引用计数值,例如,如果该对象的引用计数是 6,则 extra_rc 的值为 5