NSObject.mm源码
对象--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; // 表示该对象的引用计数值是否过大无法存储在 isa 指针
uintptr_t extra_rc : 19; // 存储引用计数值减一后的结果
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
};
};
引用计数
iOS引用计数管理之揭秘计数存储
现在一个对象的引用计数管理有三种情况:
1、TaggedPointer -- 深入理解Tagged Pointer
2、SideTable散列表
3、nonpointer( 64 位设备的指针优化)--(bits.extra_rc + SideTable)管理引用计数,(isa指针是占64比特位的,实际上只有30多位就已经够用了,为了提高利用率,剩余的比特位存储了内存管理的相关数据内容)
Tagged Pointer的对象
1、可以启用Tagged Pointer的类对象有:NSDate、NSNumber、NSString。Tagged Pointer专门用来存储小的对象。
2、在环境变量中设置OBJC_DISABLE_TAGGED_POINTERS=YES强制不启用Tagged Pointer。
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)。
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)
{
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;
}
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,
* 标记对象isa.deallocating = true,对象正在被销毁,生命周期即将结束.
* 不能再有新的 __weak 弱引用
* 调用 [self dealloc] (MRC需要在dealloc方法中手动释放强引用的变量)
* 继承关系中每一层的父类 都在调用 -dealloc,一直到根类(一般都是NSObject)
2. NSObject 调 -dealloc
* 只做一件事:调用 Objective-C runtime 中的 object_dispose() 方法
3. 调用 object_dispose()
* objc_destructInstance(obj);
* free(obj);
4. objc_destructInstance(obj)执行三个操作
* if (cxx) object_cxxDestruct(obj); // 释放变量
(1) strong的objc_storeStrong(&ivar, nil)release对象,ivar赋值nil,
(2) weak ivar,出了作用域,objc_destroyWeak(&ivar) >> storeWeak(&ivar, nil) 将ivar指向nil且ivar的地址从对象的weak表中删除。
* if (assoc) _object_remove_assocations(obj); // 移除Associate关联数据(这就是不需要手动移除的原因)
5. obj->clearDeallocating(); // 清空引用计数表、清空weak变量表且将所有引用指向nil
* 先执行 sideTable_clearDellocating()。
* 再执行 weak_clear_no_lock,在这一步骤中,会将指向该对象的弱引用指针置为 nil。
* 接下来执行 table.refcnts.eraser(),从引用计数表中擦除该对象的引用计数。
至此为止,Dealloc 的执行流程结束
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)
{
id prev = *location;
if (obj == prev) {
return;
}
objc_retain(obj);
*location = obj;
objc_release(prev);
}
解析:
1、当前strong指针指向的位置找到旧对象,
2、对新对象执行retain操作,把strong指针从新指向新对象,
3、对旧对象执行release操作。
参考:
iOS进阶——iOS(Objective-C)内存管理·二
ARC下dealloc过程及.cxx_destruct的探究