天天看点

iOS学习笔记【七】——引用计数管理iOS学习笔记【七】——引用计数管理

iOS学习笔记【七】——引用计数管理

只做简单笔记📝 详细请戳标题链接🔗

key point:为了管理所有对象的引用计数和weak指针,苹果创建了一个全局的SideTables

SideTables

  • 一个全局的Hash表,存放SideTable结构体,使用对象的内存地址作为key进行hash映射
  • 使用分离锁来保证整个hash表的安全性和操作效率,每一个元素(SideTable)都带有一把锁,降低锁的粒度,并发操作

SideTable

自旋锁

在操作引用计数的时候对SideTable加锁,避免数据错误。适用于锁使用者保持锁时间比较短的情况,自旋锁不会引起调用者睡眠,不需要进行上下文切换,减少时间开销。

引用计数器 RefcountMap

存放对象具体引用计数数量的map,同一个SideTable中可能存储多个对象管理,解决hash冲突

value具体结构

  • 最低位WEAKLY_REFERENCED:表示是否有弱引用指向这个对象,如果有的话在对象释放的时候需要把所有指向它的弱引用都变成nil,避免野指针错误
  • 次低位DEALLOCATING:表示对象是否正在被释放
  • 引用计数存储区REAL COUNT:引用计数加一或者减一,实际上是对整个value加四或者减四
  • 最高位SIDE_TABLE_RC_PINNED:随着对象的引用计数不断变大,如果这一位都变成1了,就表示引用计数已经最大了不能再增加了

维护weak指针的结构体 weak_table_t

weak_entry_t数组+数组大小,weak_entry_t数组通过循环遍历来找到对应的entry,数组大小是用来维护保证数组始终有一个合适的size

weak_entry_t结构

  • 目标对象地址referent
  • referrers可变数组:保存着所有指向这个对象的弱引用的地址,当这个对象被释放的时候,referrers里的所有指针都会被设置成nil
  • inline_referrers数组:只有4个元素的数组,默认情况下用它来存储弱引用的指针,当大于4个的时候使用referrers来存储指针
  • 在weak_entry_t 的结构定义中有联合体,在联合体的内部有定长数组inline_referrers和动态数组weak_referrer_t *referrers两种方式来存储弱引用对象的指针地址。通过out_of_line()这样一个函数方法来判断采用哪种存储方式。
  • 当弱引用该对象的指针数目小于等于WEAK_INLINE_COUNT时,使用定长数组。当超过WEAK_INLINE_COUNT时,会将定长数组中的元素转移到动态数组中,并之后都是用动态数组存储。
  • 之所以使用定长/动态数组的切换,应该是考虑到某对象弱引用的个数一般不会超过 WEAK_INLINE_COUNT 个,这时候使用定长数组不需要动态的申请内存空间,而是一次分配一块连续的内存空间,这会得到运行效率上的提升。
    iOS学习笔记【七】——引用计数管理iOS学习笔记【七】——引用计数管理

NONPOINTER_ISA

isa指针优化存储方案,用一部分额外空间存储其他内容。

define ISA_BITFIELD                                                      \
      uintptr_t nonpointer        : 1;   //指针是否优化过                                   \
      uintptr_t has_assoc         : 1;   //是否有设置过关联对象,如果没有,释放时会更快                                   \
      uintptr_t has_cxx_dtor      : 1; 	 //是否有C++的析构函数(.cxx_destruct),如果没有,释放时会更快                                     \
      uintptr_t shiftcls          : 33; //存储着Class、Meta-Class对象的内存地址信息 \
      uintptr_t magic             : 6;  //用于在调试时分辨对象是否未完成初始化                                     \
      uintptr_t weakly_referenced : 1;  //是否有被弱引用指向过,如果没有,释放时会更快                                     \
      uintptr_t deallocating      : 1;  //对象是否正在释放                                     \
      uintptr_t has_sidetable_rc  : 1;  //引用计数器是否过大无法存储在isa中                                     \
      uintptr_t extra_rc          : 19 //里面存储的值是引用计数器减1
           

extra_rc——里面存储的值是引用计数器减1

has_sidetable_rc——引用计数器是否过大无法存储在isa中(大于10),存储在sideTables中

Tagged Pointer

  • Tagged Pointer是专⻔⽤来存储⼩的对象,例如NSNumber,NSDate等。
  • Tagged Pointer指针的值不再是地址了,⽽是真正的值,它的内存并不存储在堆中
  • 当指针不够存储数据时,才会使用动态分配内存的方式来存储数据
  • 所有对象都有 isa 指针,而Tagged Pointer其实是没有的,因为它不是真正的对象。
  • Tagged Pointer通过在其最低/高 bit 位设置一个特殊标记