在OC对象的alloc过程一文中,我们一起探讨了alloc
的过程,接下来我们就对对象的retain、release、dealloc
过程进行探讨。
retain
首先我们看下 rootRetain
的源码实现
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)) {
ClearExclusive(&isa.bits);
if (rawISA()->isMetaClass()) return (id)this;
if (!tryRetain && sideTableLocked) sidetable_unlock();
if (tryRetain) return sidetable_tryRetain() ? (id)this : nil;
else return sidetable_retain();
}
// don't check newisa.fast_rr; we already called any RR overrides
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++
if (slowpath(carry)) {
// newisa.extra_rc++ overflowed
if (!handleOverflow) {
ClearExclusive(&isa.bits);
return rootRetain_overflow(tryRetain);
}
// Leave half of the retain counts inline and
// prepare to copy the other half to the side table.
if (!tryRetain && !sideTableLocked) sidetable_lock();
sideTableLocked = true;
transcribeToSideTable = true;
newisa.extra_rc = RC_HALF;
newisa.has_sidetable_rc = true;
}
} while (slowpath(!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)));
if (slowpath(transcribeToSideTable)) {
// Copy the other half of the retain counts to the side table.
sidetable_addExtraRC_nolock(RC_HALF);
}
if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock();
return (id)this;
}
首先,会判断当前 是否为 taggedPointer
,如果是就直接返回。然后会操作散列表(SideTable)
,在 sidetable_unlock
方法中
void
objc_object::sidetable_unlock()
{
SideTable& table = SideTables()[this];
table.unlock();
}
从 SideTablesMap
中以this
为key
得到该散列表,在内存中有多个散列表,是通过Map
进行存放的,一共有 64张散列表。为什么会有多张散列表呢?因为我们对引用计数进行管理时,都需要解锁上锁,性能不好。
在 SideTable
结构体中
struct SideTable {
spinlock_t slock;
RefcountMap refcnts;
weak_table_t weak_table;
SideTable() {
memset(&weak_table, 0, sizeof(weak_table));
}
~SideTable() {
_objc_fatal("Do not delete SideTable.");
}
void lock() { slock.lock(); }
void unlock() { slock.unlock(); }
void forceReset() { slock.forceReset(); }
// Address-ordered lock discipline for a pair of side tables.
template
static void lockTwo(SideTable *lock1, SideTable *lock2);
template
static void unlockTwo(SideTable *lock1, SideTable *lock2);
};
有一把锁 spinlock_t
,存放引用对象的引用计数表RefcountMap
,和一张弱引用表weak_table_t
。
第二步判断当前对象是否在deallocing状态,如果是正在dealloc,流程就进入到了 dealloc
阶段,dealloc的过程在文章的下个阶段有介绍。
第三步,对isa的bits进行处理,对 extra_rc
进行 +1
,如果 extra_rc
满了,就操作散列表。将满状态extra_rc的一半移到散列表中
,将 has_sidetable_rc
状态位设置为 true
,表示现在依赖散列表了。
release
release 的过程和retain的过程类似,retain是对extra_rc进行相加,release是对extra_rc相减。
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)) {
ClearExclusive(&isa.bits);
if (rawISA()->isMetaClass()) return false;
if (sideTableLocked) sidetable_unlock();
return sidetable_release(performDealloc);
}
// don't check newisa.fast_rr; we already called any RR overrides
uintptr_t carry;
newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry); // extra_rc--
if (slowpath(carry)) {
// don't ClearExclusive()
goto underflow;
}
} while (slowpath(!StoreReleaseExclusive(&isa.bits,
oldisa.bits, newisa.bits)));
if (slowpath(sideTableLocked)) sidetable_unlock();
return false;
underflow:
......
}
对extra_rc
进行减法运算使用的是 subc
函数,如果extra_rc为0(empty)
了,就会在散列表里面进行查找减1。当extra_rc
为空且散列表里面的也为空,则将 deallocing 设置为 true
,对该对象进行析构(dealloc)
。
root_dealloc
其源码实现如下
inline void
objc_object::rootDealloc()
{
if (isTaggedPointer()) return; // fixme necessary?
if (fastpath(isa.nonpointer &&
!isa.weakly_referenced &&
!isa.has_assoc &&
!isa.has_cxx_dtor &&
!isa.has_sidetable_rc))
{
assert(!sidetable_present());
free(this);
}
else {
object_dispose((id)this);
}
}
对象在释放的时候,要 清除对象的关联对象,调用其cxx析构函数,清空弱引用表,清空散列表里面的引用计数表,最后进行free
在objc_destructInstance
方法中,如果有弱引用(weak_reference)
会将其弱引用表进行清空(erase)
。如果有has_sidetable_rc
,将其存放引用计数的sidetable
清空。