iOS 底层 - 内存管理之引用计数存储
本文源自本人的学习记录整理与理解,其中参考阅读了部分优秀的博客和书籍,尽量以通俗简单的语句转述。引用到的地方如有遗漏或未能一一列举原文出处还望见谅与指出,另文章内容如有不妥之处还望指教,万分感谢 !
写在前面:
自从64位操作系统以后,引用计数被直接存储在优化过的ISA(共用体
)指针的extra_rc
中,extra_rc
拥有19位的内存来存储引用计数减一,
随着持有者的增多引用计数也随之增大,当引用计数值过大,extra_rc
中无法存储时,就会把引用计数存储到一个SideTable
类的refcnts
属性中;
图解:
引用计数存储.pngSideTable本质上是一个结构体,如下图:
- 源码定义
struct SideTable {
spinlock_t slock;
RefcountMap refcnts;
weak_table_t weak_table;
}
-
slock
: 自旋锁,用来操作SideTable时加锁解锁 -
refcnts
: 是Reference Counting的简写,是一个存放着对象引用计数的散列表
; -
weak_table
: 散列表, 存储指向OC对象弱引用指针
这一顿操作猛如虎,我咋还晕着呢 ?SideTable到底是个啥 ?
不要慌!不要慌!不要慌!😁😁😁😁😁😁😁
- 为了管理程序中所有对象的
引用计数
和weak指针
,runtime创建了一个全局的SideTables
,虽然名字后面有个"s"不过他其实是一个全局的Hash表,里面的内容装的都是SideTable
结构体而已。它使用对象的内存地址当它的key
。来管理引用计数和weak指针。
获取一个OC对象的引用计数方法是什么 ?
MRC环境下直接调用对象的retaionCount
方法获取
此方法在调试内存管理问题时没有价值。
因为许多框架对象可能保留了一个对象来保存对它的引用,而同时autorelease池可能保存了一个对象上的任意数量的延迟发布,
所以您不太可能从这个方法中获得有用的信息。
- (NSUInteger)retainCount OBJC_ARC_UNAVAILABLE;
注意:只有在对象没有被添加在自动缓存池且没有被其他框架引用的情况下,返回的值才绝对有效
ARC环境使用CoreFoundation中
的CFGetRetainCount()
获取
此函数可能有助于调试内存泄漏,返回核心基础对象的引用计数
CFIndex CFGetRetainCount(CFTypeRef cf);
有存储肯定也有擦除
擦除的具体实现在runtime底层源码的NSObject.mm文件的:void objc_object::clearDeallocating_slow()
中
- 核心方法:table.refcnts.erase
- erase:擦除
- 实现逻辑:找到当前对象对应的引用计数表中的对象,
for循环free掉它 !
需要了解全部过程请移步iOS 底层 - 内存管理之weak指针
NEVER_INLINE void
objc_object::clearDeallocating_slow()
{
ASSERT(isa.nonpointer && (isa.weakly_referenced || isa.has_sidetable_rc));
//获取当前obj对应的SideTable
SideTable& table = SideTables()[this];
//加锁
table.lock();
//有weak指针指向
if (isa.weakly_referenced) {
//传入弱引用表和当前对象,清空
weak_clear_no_lock(&table.weak_table, (id)this);
}
//引用计数存储在sidetable中
if (isa.has_sidetable_rc) {
//传入当前对象,擦除存储
table.refcnts.erase(this);
}
//解锁
table.unlock();
}
用lldb打印一个对象的引用计数
NSObject *obj = [[NSObject alloc] init];
在obj实例化后打断点,并在lldb中输出isa来验证:
(lldb) p obj
(NSObject *) 0
0x10064b810: 0x001d800100350141 0x0000000000000000
0x10064b820: 0x64696c53534e5b2d 0x206b636172547265
//打印isa的值
(lldb) p 0x001d800100350141
(long) 1
(isa_t) $2 = {
cls = NSObject
bits = 8303516111405377
= {
nonpointer = 1
has_assoc = 0
has_cxx_dtor = 0
shiftcls = 537305128
magic = 59
weakly_referenced = 0
deallocating = 0
has_sidetable_rc = 0
extra_rc = 0
}
}
(lldb)