散列表
散列表其实是一个哈希表,下面挂载了64张表,每张表都包含自旋锁,引用计数表,弱引用表
- 自旋锁:忙等,如果锁已被其他线程获取,那么当前线程会自己去不断的获取是否被释放,直到其他线程释放,适用于轻量访问,如+1,-1。
- 引用计数表:hash查找,提高查找效率,插入和查找通过同一个hash函数来获取,避免了循环遍历。ptr->hash->size_t,其中的size_t就是引用计数值,比如用64位存储,第一位表示(weakly_referenced),表示对象是否存在弱引用,下一位表示当前对象是都正在dealoc(deallocating),剩下的位表示引用计数值。
- 弱引用表:也是一个hash表,key->hash查找->weak_entry_t,weak_entry_t其实是一个结构体数组(weakPtr),比如被weak修饰,就存在这个弱引用表中。
引用计数存储
如果引用计数小于19个二进制位,则存储在isa指针的extra_rc;如果引用计数过大,则isa中的has_sidetable_rc就为1,引用计数就存储在一个叫SideTable的结构体的refcnts成员中,refcnts是个散列表。
ARC即自动引用计数
使用ARC应遵循原则
- 不能使用retain、release、autorelease、retainCount
- 不能使用NSAllocateObject以及NSDeallocateObject
- 必须遵循内存管理方法的命名规则
- 不需要显式调用dealloc
- 不能使用NSZone
- 对象型变量不能作为C结构体成员
- 显式转换id以及void*
ARC自动内存管理原则———谁持有谁释放
- 自己生成的自己可以持有
- 非自己生成的自己可以持有
- 自己持有的不再需要时自己要对其释放
- 非自己持有的对象自己无法对其释放
weak
weak实现
Runtime维护了一个全局的CFMutableDictionary,来保存对象的weak指针列表。key为所指对象的内存地址,由于每个对象可能有多个weak指针,value则为weak指针的地址集合(CFMutableSet类型)。
weak实现原理
-
初始化时:runtime调用objc_initWeak函数,objc_initWeak函数会初始化一个新的weak指针指向对象地址。
objc_initWeak函数有一个前提条件:就是object必须是一个没有被注册为__weak对象的有效指针。而value则可以是null,或者指向一个有效的对象
添加引用时:objc_initWeak函数调用objc_storeWeak()函数,objc_storeWeak()的作用是更新指针指向,创建对应的弱引用表(新旧表比对)
-
释放时:调用clearDeallocating函数。clearDeallocating先根据对象地址获取所有weak指针的集合,然后遍历集合将其中数据设为nil,最后把这个entry从weak表删除,最后做清理的记录
当weak引用的对象被释放时,对于weak指针的处理流程如下:
- 调用objc_release
- 由于引用计数为0,执行dealloc
- dealloc中调用_objc_rootDealloc
- _objc_rootDealloc中调用object_dispose
- 调用objc_destructInstance
- 调用objc_clear_deallocating,objc_clear_deallocating流程如下:
- 从weak表中,以dealloc对象为key,找到对应weak_entry_t集合
- 将weak_entry_t集合中的所有附有 weak修饰符变量的地址,赋值为nil
- 将weak表中该对象移除