对象--id
typedef struct objc_object *id;
struct objc_object {
isa_t _Nonnull isa OBJC_ISA_AVAILABILITY;
};
arm64 架构中的 isa_t 结构体 (bits格式一样,一些信息的位数不一样)
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
struct {
uintptr_t nonpointer : 1; // 0 表示普通的 isa 指针,1 表示使用优化,存储引用计数
uintptr_t has_assoc : 1; // 表示该对象是否包含 associated object,如果没有,则析构时会更快
uintptr_t has_cxx_dtor : 1; // 表示该对象是否有 C++ 或 ARC 的析构函数,如果没有,则析构时更快
uintptr_t shiftcls : 33; // 类的指针
uintptr_t magic : 6; // 固定值为 0xd2,用于在调试时分辨对象是否未完成初始化。
uintptr_t weakly_referenced : 1; // 表示该对象是否有过 weak 对象,如果没有,则析构时更快
uintptr_t deallocating : 1; // 表示该对象是否正在析构
uintptr_t has_sidetable_rc : 1; // 标记是否在 sidetable 中存有引用计数
uintptr_t extra_rc : 19; // 存储引用计数值减一后的结果
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
};
};
引用计数
现在一个对象的引用计数管理有三种情况:
1、TaggedPointer -- 深入理解Tagged Pointer
2、非nonpointer -- 纯SideTable管理引用计数
3、nonpointer( 64 位设备的指针优化)--(bits.extra_rc + SideTable)管理引用计数,(isa指针是占64位的,类的指针bits.shiftcls只占用33位,为了提高利用率,剩余的存储内存管理的相关数据内容)
TaggedPointer的对象
对于 64 位程序,为了节省内存和提高执行效率,苹果提出了Tagged Pointer的概念。
1、可以启用Tagged Pointer的类对象有:NSDate、NSNumber、NSString。Tagged Pointer专门用来存储小的对象。
2、Tagged Pointer指针的值不再是地址了,而是真正的值。所以,实际上它不再是一个对象了,它只是一个披着对象皮的普通变量而已。创建读取效率非常快。
3、在环境变量中设置OBJC_DISABLE_TAGGED_POINTERS=YES强制不启用Tagged Pointer。
nonpointer
nonpointer:在64位系统中,为了降低内存使用,提升性能,isa中有一部分字段用来存储其他信息。
RetainCount源码,直接体现了三种情况
inline uintptr_t
objc_object::rootRetainCount()
{
if (isTaggedPointer()) return (uintptr_t)this; // ❤️1、TaggedPointer
sidetable_lock();
isa_t bits = LoadExclusive(&isa.bits);
ClearExclusive(&isa.bits);
if (bits.nonpointer) { // ❤️3、nonpointer--(bits.extra_rc + SideTable)管理引用计数
uintptr_t rc = 1 + bits.extra_rc;
if (bits.has_sidetable_rc) {
rc += sidetable_getExtraRC_nolock();
}
sidetable_unlock();
return rc;
}
sidetable_unlock();
return sidetable_retainCount(); // ❤️2、纯SideTable
}
2、SideTable
内存管理主要结构代码
struct SideTable {
spinlock_t slock; // 保证原子操作的自旋锁
RefcountMap refcnts; // 引用计数的 hash 表
weak_table_t weak_table; // weak 引用全局 hash 表
};
retainCount 是保存在一个无符号整形中
uintptr_t
objc_object::sidetable_retainCount()
{
SideTable& table = SideTables()[this];
size_t refcnt_result = 1;
table.lock();
RefcountMap::iterator it = table.refcnts.find(this);
if (it != table.refcnts.end()) {
// it->second 无符号整型(上面的图),真实的引用计数需要右移2位
refcnt_result += it->second >> SIDE_TABLE_RC_SHIFT;
}
table.unlock();
return refcnt_result;
}
retain源码
id
objc_object::sidetable_retain()
{
#if SUPPORT_NONPOINTER_ISA
assert(!isa.nonpointer);
#endif
SideTable& table = SideTables()[this];
table.lock();
size_t& refcntStorage = table.refcnts[this];
// 判断是否溢出,溢出了就是引用计数太大了,不管理了
if (! (refcntStorage & SIDE_TABLE_RC_PINNED)) {
// 引用计数加1,(上图可知,偏移2位,SIDE_TABLE_RC_ONE = 1<<2)
refcntStorage += SIDE_TABLE_RC_ONE;
}
table.unlock();
return (id)this;
}
release源码
uintptr_t
objc_object::sidetable_release(bool performDealloc)
{
#if SUPPORT_NONPOINTER_ISA
assert(!isa.nonpointer);
#endif
SideTable& table = SideTables()[this];
bool do_dealloc = false;
table.lock();
RefcountMap::iterator it = table.refcnts.find(this);
if (it == table.refcnts.end()) {
// ❤️(1)对象没有引用计数(没有被强引用)
do_dealloc = true;
// 标记dealloc(上图的第二位)
table.refcnts[this] = SIDE_TABLE_DEALLOCATING;
} else if (it->second < SIDE_TABLE_DEALLOCATING) {
// ❤️(2)引用计数为0
do_dealloc = true;
// 标记dealloc(上图的第二位,或运算),
it->second |= SIDE_TABLE_DEALLOCATING;
} else if (! (it->second & SIDE_TABLE_RC_PINNED)) {
// ❤️(3)正常引用计数减1,(上图可知,偏移2位,SIDE_TABLE_RC_ONE = 1<<2)
it->second -= SIDE_TABLE_RC_ONE;
}
table.unlock();
if (do_dealloc && performDealloc) {
// ❤️(4)do_dealloc为真,执行dealloc方法
((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
}
return do_dealloc;
}
看后面几个判断。
(1) 如果对象记录在引用计数表的最后一个(对象没有引用计数):do_dealloc 设置为 true,引用计数数值设置为 SIDE_TABLE_DEALLOCATING(二进制 00000010)。
(2) 如果引用计数小于 SIDE_TABLE_DEALLOCATING(就是引用计数等于0),标记dealloc。
(3) 如果引用计数大于>=1, 就it->second -= SIDE_TABLE_RC_ONE;就是-1。
(4) 如果 do_dealloc 和 performDealloc(传入时就已经为 true)都为 ture,执行 SEL_dealloc 释放对象。方法返回 do_dealloc。
(5)调用父类dealloc,直到根类NSobject
3、nonpointer(bits.extra_rc + SideTable)
retain源码
ALWAYS_INLINE id
objc_object::rootRetain(bool tryRetain, bool handleOverflow)
{
//❤️❤️❤️ 1、TaggedPointer
if (isTaggedPointer()) return (id)this;
bool sideTableLocked = false;
bool transcribeToSideTable = false;
isa_t oldisa;
isa_t newisa;
do {
transcribeToSideTable = false;
oldisa = LoadExclusive(&isa.bits);
newisa = oldisa;
if (slowpath(!newisa.nonpointer)) {
//❤️❤️❤️ 2、纯SideTable散列表方法
ClearExclusive(&isa.bits);
if (!tryRetain && sideTableLocked) sidetable_unlock();
if (tryRetain) return sidetable_tryRetain() ? (id)this : nil;
else return sidetable_retain();
}
if (slowpath(tryRetain && newisa.deallocating)) {
// 正在释放
ClearExclusive(&isa.bits);
if (!tryRetain && sideTableLocked) sidetable_unlock();
return nil;
}
//❤️❤️❤️ 3、nonpointer(isa.extra_rc+ SideTable)
uintptr_t carry;
newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry); // 应用计数extra_rc++
// 如果newisa.extra_rc++ 溢出, carry==1
if (slowpath(carry)) {
// 溢出
if (!handleOverflow) {
ClearExclusive(&isa.bits); // 空操作(系统预留)
return rootRetain_overflow(tryRetain);// 再次调用rootRetain(tryRetain,YES)
}
// 执行rootRetain_overflow会来到这里,就把extra_rc对应的数值(一半)存到SideTable
if (!tryRetain && !sideTableLocked) sidetable_lock();
sideTableLocked = true;
transcribeToSideTable = true;
newisa.extra_rc = RC_HALF; // 溢出了,设置为一半,保存一半到SideTable
newisa.has_sidetable_rc = true; // 标记借用SideTable存储
}
// StoreExclusive保存newisa.bits到isa.bits,保存成功返回YES
// 这里while判断一次就结束了
} while (slowpath(!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)));
if (slowpath(transcribeToSideTable)) {
// 借位保存:把RC_HALF(一半)存入SideTable
sidetable_addExtraRC_nolock(RC_HALF);
}
if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock();
return (id)this;
}
这个方法挺简单的:
1、其实就是引用计数extra_rc++;
2、extra_rc满了存一半到SideTable(arm64的extra_rc是19位,完全够存)
借位保存方法
bool
objc_object::sidetable_addExtraRC_nolock(size_t delta_rc)
{
assert(isa.nonpointer);
SideTable& table = SideTables()[this];
size_t& refcntStorage = table.refcnts[this];
size_t oldRefcnt = refcntStorage;
// isa-side bits should not be set here
assert((oldRefcnt & SIDE_TABLE_DEALLOCATING) == 0);
assert((oldRefcnt & SIDE_TABLE_WEAKLY_REFERENCED) == 0);
// 系统计数极限了,直接true
if (oldRefcnt & SIDE_TABLE_RC_PINNED) return true;
uintptr_t carry;
size_t newRefcnt =
addc(oldRefcnt, delta_rc << SIDE_TABLE_RC_SHIFT, 0, &carry);
if (carry) {
// 如果借位保存在这里还溢出,就当做SIDE_TABLE_RC_PINNED次数(32或64最大位数-1的数值)
refcntStorage =
SIDE_TABLE_RC_PINNED | (oldRefcnt & SIDE_TABLE_FLAG_MASK);
return true;
}
else {
refcntStorage = newRefcnt;
return false;
}
}
release源码
ALWAYS_INLINE bool
objc_object::rootRelease(bool performDealloc, bool handleUnderflow)
{
if (isTaggedPointer()) return false;
bool sideTableLocked = false;
isa_t oldisa;
isa_t newisa;
retry:
do {
oldisa = LoadExclusive(&isa.bits);
newisa = oldisa;
if (slowpath(!newisa.nonpointer)) {
// 这是2、SideTable散列表的
ClearExclusive(&isa.bits);
if (sideTableLocked) sidetable_unlock();
return sidetable_release(performDealloc);
}
uintptr_t carry;
newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry); // extra_rc--
// 如果extra_rc==0,extra_rc--会是负数,carry=1
if (slowpath(carry)) {
goto underflow;
}
} while (slowpath(!StoreReleaseExclusive(&isa.bits,
oldisa.bits, newisa.bits)));
if (slowpath(sideTableLocked)) sidetable_unlock();
return false;
underflow:
// newisa重新赋值
newisa = oldisa;
if (slowpath(newisa.has_sidetable_rc)) {
// 有借位保存,rootRelease_underflow重新进入函数
if (!handleUnderflow) {
ClearExclusive(&isa.bits);
return rootRelease_underflow(performDealloc);
}
// 一些锁的操作
if (!sideTableLocked) {
ClearExclusive(&isa.bits);
sidetable_lock();
sideTableLocked = true;
goto retry;
}
// 获取借位引用次数,(获取次数最大RC_HALF)
size_t borrowed = sidetable_subExtraRC_nolock(RC_HALF);
if (borrowed > 0) {
// extra_rc--
newisa.extra_rc = borrowed - 1;
// 保存,就是StoreExclusive
// 如果&isa.bits和oldisa.bits相等,那么就把newisa.bits的值赋给&isa.bits,并且返回true
bool stored = StoreReleaseExclusive(&isa.bits,
oldisa.bits, newisa.bits);
if (!stored) {
// 保存失败,重新试一次(重复的代码)
isa_t oldisa2 = LoadExclusive(&isa.bits);
isa_t newisa2 = oldisa2;
if (newisa2.nonpointer) {
uintptr_t overflow;
newisa2.bits =
addc(newisa2.bits, RC_ONE * (borrowed-1), 0, &overflow);
if (!overflow) {
stored = StoreReleaseExclusive(&isa.bits, oldisa2.bits,
newisa2.bits);
}
}
}
if (!stored) {
// 还是不成功,把次数放回SideTable,重试retry
sidetable_addExtraRC_nolock(borrowed);
goto retry;
}
// release结束
sidetable_unlock();
return false;
}
else {
// 如果sidetable也有没有次数,然后就到下面dealloc阶段了
}
}
// 如果没有借位保存次数,来到这里
if (slowpath(newisa.deallocating)) {
// 如果对象已经正在释放,报错警告:多次release
ClearExclusive(&isa.bits);
if (sideTableLocked) sidetable_unlock();
return overrelease_error();
}
newisa.deallocating = true;
// 保存bits
if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;
if (slowpath(sideTableLocked)) sidetable_unlock();
__sync_synchronize();
if (performDealloc) {
// 调用dealloc
((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
}
return true;
}
NSobject dealloc源码
void *objc_destructInstance(id obj)
{
if (obj) {
bool cxx = obj->hasCxxDtor();
bool assoc = obj->hasAssociatedObjects();
if (cxx) object_cxxDestruct(obj);
if (assoc) _object_remove_assocations(obj);
obj->clearDeallocating();
}
return obj;
}
简单明确的干了三件事:
1、执行object_cxxDestruct,处理本类的成员变量(父类的由父类处理)。
object_cxxDestruct里面最终for ( ; cls; cls = cls->superclass){执行.cxx_destruct};
cxx_destruct最后执行处理成员变量:
① strong的objc_storeStrong(&ivar, nil)release对象,ivar赋值nil,
② weak的objc_destroyWeak(& ivar)消除对象weak表中的ivar地址。
2、执行_object_remove_assocations去除和这个对象assocate的对象(常用于category中添加带变量的属性,这也是为什么没必要remove一遍的原因。
3、执行objc_clear_deallocating,清空引用计数表并清除弱引用表,将所有weak引用指nil(这也就是weak变量能安全置空的所在)。
对象释放过程的简单总结--dealloc
1. 调用 -release :isa.bits.extra_rc由0继续减一时候触发dealloc,
(1)标记对象isa.deallocating = true,对象正在销毁,生命周期即将结束,不能再有新的 weak 弱引用
(2)调用 [self dealloc] (MRC需要在dealloc方法中手动释放强引用的变量)
(3)superclass都调用dealloc,一直到根类(NSObject)
2. 根类 NSObject 调 dealloc -> object_dispose()
(1)objc_destructInstance(obj);
(2)free(obj);
3. objc_destructInstance(obj)执行三个操作
(1)if (cxx) object_cxxDestruct(obj); // 释放变量
① strong ivar 执行objc_storeStrong(&ivar, nil)release对象,ivar赋值nil,
② weak ivar 执行objc_destroyWeak(&ivar) >> storeWeak(&ivar, nil) 将ivar指向nil且ivar的地址从对象的weak表中删除。
(2)if (assoc) _object_remove_assocations(obj); // 移除Associate关联数据(这就是不需要手动移除的原因)
(3)obj->clearDeallocating(); // 清空weak变量表且将所有引用指向nil、清空引用计数表
① 执行 weak_clear_no_lock:清空weak变量表且将所有引用指向nil
② 执行 table.refcnts.eraser:清空相关SideTable的引用计数表。
objc_storeStrong源码
是对一个strong指针再次赋值,比如
NSObject *obj = [NSObject new];
NSObject *obj2 = [NSObject new];
NSObject *strongObj = obj;
strongObj = obj2;//执行objc_storeStrong(&strongObj, obj2);
{
NSObject *obj = [NSObject new];
} // 出了作用域,执行objc_storeStrong(&obj, nil);
void
objc_storeStrong(id *location, id obj)
{
// 1、当前strong指针指向的位置找到旧对象,
id prev = *location;
if (obj == prev) {
return;
}
objc_retain(obj);// 2、新对象执行retain,指针指向新对象,
*location = obj;
objc_release(prev); //3、 release旧对象
}
参考:
iOS进阶——iOS(Objective-C)内存管理·二
ARC下dealloc过程及.cxx_destruct的探究