一、引用计数值存储位置
OC对象的引用计数值存储在对象的isa指针中,isa的结构如下:
isa结构体:
union isa_t
{
struct {
...
uintprt_t has_sidetable_rc; // 引用计数是否存在sidetable中
uintprt_t extra_rc ; // 存储的值为引用计数值-1
}
}
如果has_sidetable_rc==0,引用计数值就存储在extra_rc中;如果has_sidetable_rc ==1,表示计数值超出了extra_rc 的存储范围(19个字节),此时计数值存储在sidetable中。
sideTable结构:
struct SideTable {
spinlock_t slock;
RefcountMap refcnts;
weak_table_t weak_table;
}
sideTable的refcnts存储着引用计数,refcnts是个散列表,存储数据是以对象地址为key,引用计数值为value存储。sideTable是静态数据,获取某个对象引用计数值时,通过对象地址,在静态的sideTable表中进行查找。
二、如何做到自动释放的
- 工程的main入口有一个autoReleasePool,每个线程内部也自带了autoReleasePool。主线程的runloop循环时会创建一个autoreleasePool,结束时会会触发pool里的内存释放逻辑,处理完成后销毁该autoreleasePool,下个循环开始时会创建新的pool。子线程不会一直创建、销毁pool。
- arc下的对象初始化后,编译器会自动往后面追加autorelease标识,程序运行时就会把这个对象存储在最近的autoReleasePool中。autoReleasePool要倾倒时,会检测里面的对象是否有引用计数为0的,有则触发改对象的dealloc逻辑
三、Dealloc
引用计数为0时,会触发dealloc方法,dealloc过程是先子类->父类...->NSObject。
dealloc做的工作包括:
1. C++函数释放: objc_cxxDestruct
2. 移除关联属性:_object_remove_assocations
3. 将弱引用自动设置nil: weak_clear_no_lock(&table.weak_table,(id)this)
4. 引用计数处理:table.refcnts.erase(this)
5. 销毁对象:free(obj)
网友评论