- 本文主要分析几个与对象内存管理相关的几个函数的底层源码实现,包括
retain
,release
,dealloc
和retainCount
obj->retain()源码
inline id objc_object::retain(){
ASSERT(!isTaggedPointer());
if (fastpath(!ISA()->hasCustomRR())) {
return rootRetain();
}
return ((id(*)(objc_object *, SEL))objc_msgSend)(this, @selector(retain));
}
ALWAYS_INLINE id
objc_object::rootRetain(){
return rootRetain(false, false);
}
ALWAYS_INLINE id objc_object::rootRetain(bool tryRetain, bool handleOverflow){
//若是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;
//如果isa没有使用优化,直接操作散列表,引用计数+1
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();
}
//deallocating 是优化的isa指针的属性二进制位
//如果对象正在释放,则执行dealloc流程,释放弱引用表和引用计数表
//最后free释放对象内存
if (slowpath(tryRetain && newisa.deallocating)) {
ClearExclusive(&isa.bits);
if (!tryRetain && sideTableLocked) sidetable_unlock();
return nil;
}
//carry 用来标识经过优化的isa指针的extra_rc(引用计数)是否已满
uintptr_t carry;
//经过优化的isa指针 extra_rc++ 即引用计数+1
newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry); // extra_rc++
//若isa指针的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;
//如果extra_rc满了,则直接将满状态的一半拿出来存到extra_rc
newisa.extra_rc = RC_HALF;
//给一个标识符为YES,表示需要存储到散列表
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.
//将另一半存在散列表的rc_half中,即满状态下是8位,一半就是1左移7位,即除以2
//这么操作的目的在于提高性能:
//因为如果都存在散列表中,当需要release-1时,需要去访问散列表,每次都需要开解锁,比较消耗性能。
//extra_rc存储一半的话,可以直接操作extra_rc即可,不需要操作散列表。性能会提高很多
sidetable_addExtraRC_nolock(RC_HALF);
}
if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock();
return (id)this;
}
- 具体逻辑步骤如下:
- 第1步:若对象为
TaggedPointer小对象
,无需进行内存管理,直接返回;
- 第2步:若对象的isa没有经过优化,即
!newisa.nonpointer
成立,由于tryRetain=false,直接进入sidetable_retain
方法,此方法本质是直接操作散列表,最后让目标对象的引用计数+1
;
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)) {
refcntStorage += SIDE_TABLE_RC_ONE;
}
table.unlock();
return (id)this;
}
-
SideTable& table = SideTables()[this]
根据目标对象本身,在散列表集合SideTables中获取到对应的散列表,由于散列表操作是线程安全的,所以需要进行解锁即table.lock()
,散列表集合的结构体如下所示:
template
class StripedMap {
#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
enum { StripeCount = 8 };
#else
enum { StripeCount = 64 };
#endif
struct PaddedT {
T value alignas(CacheLineSize);
};
PaddedT array[StripeCount];
.....
}
- SideTables顾名思义是一个散列表的集合,其类型为
StripedMap
;
- 可以看出在iOS或者iOS模拟器架构下,内存中最多只会存在8张散列表;
- 散列表的结构体如下所示:
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(); }
......
}
- 看到散列表的三个成员:
slock
是线程锁,refcnts
是引用计数表,weak_table
是弱引用表;
-
size_t& refcntStorage = table.refcnts[this]
:根据对象获取散列表的引用计数表中的引用计数refcntStorage,然后执行+1操作;执行完成时,再上锁;
- 第3步:判断对象是否正在释放,若正在正在释放,则执行dealloc流程,释放弱引用表和引用计数表;
- 第4步:若对象的isa经过了优化,则执行
newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry)
,即isa的位域extra_rc+1,且通过变量carry
来判断位域extra_rc
是否已满,如果位域extra_rc已满则执行newisa.extra_rc = RC_HALF
,即将extra_rc满状态的一半拿出来存到extra_rc位域中,然后将另一半存储到散列表中,执行sidetable_addExtraRC_nolock(RC_HALF)函数
;
obj->release()源码
__attribute__((aligned(16), flatten, noinline))
void objc_release(id obj){
if (!obj) return;
if (obj->isTaggedPointer()) return;
return obj->release();
}
inline void objc_object::release(){
ASSERT(!isTaggedPointer());
if (fastpath(!ISA()->hasCustomRR())) {
rootRelease();
return;
}
((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(release));
}
ALWAYS_INLINE bool objc_object::rootRelease(){
return rootRelease(true, false);
}
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)) {
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:
// newisa.extra_rc-- underflowed: borrow from side table or deallocate
// abandon newisa to undo the decrement
newisa = oldisa;
//判断对象在散列表中是否存在引用计数
if (slowpath(newisa.has_sidetable_rc)) {
if (!handleUnderflow) {
ClearExclusive(&isa.bits);
return rootRelease_underflow(performDealloc);
}
// Transfer retain count from side table to inline storage.
if (!sideTableLocked) {
ClearExclusive(&isa.bits);
sidetable_lock();
sideTableLocked = true;
// Need to start over to avoid a race against
// the nonpointer -> raw pointer transition.
goto retry;
}
// Try to remove some retain counts from the side table.
//获取散列表中对象的引用计数的一半
size_t borrowed = sidetable_subExtraRC_nolock(RC_HALF);
// To avoid races, has_sidetable_rc must remain set
// even if the side table count is now zero.
if (borrowed > 0) {
// Side table retain count decreased.
// Try to add them to the inline count.
newisa.extra_rc = borrowed - 1; //redo the original decrement too
bool stored = StoreReleaseExclusive(&isa.bits,
oldisa.bits, newisa.bits);
if (!stored) {
// Inline update failed.
// Try it again right now. This prevents livelock on LL/SC
// architectures where the side table access itself may have
// dropped the reservation.
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) {
// Inline update failed.
// Put the retains back in the side table.
sidetable_addExtraRC_nolock(borrowed);
goto retry;
}
// Decrement successful after borrowing from side table.
// This decrement cannot be the deallocating decrement - the side
// table lock and has_sidetable_rc bit ensure that if everyone
// else tried to -release while we worked, the last one would block.
sidetable_unlock();
return false;
}else{
// Side table is empty after all. Fall-through to the dealloc path.
}
}
//来到这里说明 isa的extra_rc引用计数为0且散列表中也为0,即引用计数为0,进入dealloc流程
if (slowpath(newisa.deallocating)) {
ClearExclusive(&isa.bits);
if (sideTableLocked) sidetable_unlock();
return overrelease_error();
}
newisa.deallocating = true;
if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;
if (slowpath(sideTableLocked)) sidetable_unlock();
__c11_atomic_thread_fence(__ATOMIC_ACQUIRE);
//执行对象的dealloc方法
if (performDealloc) {
((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(dealloc));
}
return true;
}
- 第1步:若对象为
TaggedPointer小对象
,不需要做内存管理操作,直接返回;
- 第2步:若对象的isa没有经过优化,即
!newisa.nonpointer
成立,直接进入sidetable_release
方法,此方法本质是直接操作散列表,最后让目标对象的引用计数-1;实现如下:
uintptr_tobjc_object::sidetable_release(bool performDealloc){
#if SUPPORT_NONPOINTER_ISA
ASSERT(!isa.nonpointer);
#endif
SideTable& table = SideTables()[this];
bool do_dealloc = false;
table.lock();
auto it = table.refcnts.try_emplace(this, SIDE_TABLE_DEALLOCATING);
auto &refcnt = it.first->second;
if (it.second) {
do_dealloc = true;
} else if (refcnt < SIDE_TABLE_DEALLOCATING) {
// SIDE_TABLE_WEAKLY_REFERENCED may be set. Don't change it.
do_dealloc = true;
refcnt |= SIDE_TABLE_DEALLOCATING;
} else if (! (refcnt & SIDE_TABLE_RC_PINNED)) {
refcnt -= SIDE_TABLE_RC_ONE;
}
table.unlock();
if (do_dealloc && performDealloc) {
((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(dealloc));
}
return do_dealloc;
}
- 访问对象所在的散列表,再获取对象的引用计数表,最后获取到对象的引用计数
refcnt
,执行refcnt -= SIDE_TABLE_RC_ONE
,即引用计数-1;
- 在引用计数-1之后,会检测是否满足dealloc条件(引用计数为0),若满足条件,对象执行dealloc流程;
- 第3步:若对象的isa经过优化,则执行
newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry)
,即对象的isa位域extra_rc-1;且通过变量carry
标识对象的isa的extra_rc是否为0, 如果对象的isa的extra_rc=0,则去访问散列表
,判断对象在散列表中是否存在引用计数;
- 若对象在散列表中没有引用计数,表明对象的isa的extra_rc=0且sideTable的引用计数为0,
综合之下对象的引用计数为0
,则对象执行dealloc流程;
- 若对象在散列表中有引用计数,执行
size_t borrowed = sidetable_subExtraRC_nolock(RC_HALF)
,即将散列表中一半引用计数borrowed取出来,然后borrowed-1再赋值给对象的isa的位域extra_rc,即newisa.extra_rc = borrowed - 1
;
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);
}
}
- 当对象的isa经过优化,且没有弱引用表,关联其他对象,引用计数表的情况下,直接释放内存即可,执行
free(this)
,否则执行object_dispose((id)this)
;
id object_dispose(id obj){
if (!obj) return nil;
objc_destructInstance(obj);
free(obj);
return nil;
}
- 内部执行
objc_destructInstance(obj)
函数,最后再释放对象的内存;
void *objc_destructInstance(id obj) {
if (obj) {
// Read all of the flags at once for performance.
bool cxx = obj->hasCxxDtor();
bool assoc = obj->hasAssociatedObjects();
// This order is important.
if (cxx) object_cxxDestruct(obj);
if (assoc) _object_remove_assocations(obj);
obj->clearDeallocating();
}
return obj;
}
-
if (cxx) object_cxxDestruct(obj)
:调用C++的析构函数,销毁对象;
-
if (assoc) _object_remove_assocations(obj)
:移除对象的关联对象;
- 最后执行
obj->clearDeallocating()
函数;
inline void objc_object::clearDeallocating(){
if (slowpath(!isa.nonpointer)) {
// Slow path for raw pointer isa.
sidetable_clearDeallocating();
}
else if (slowpath(isa.weakly_referenced || isa.has_sidetable_rc)) {
// Slow path for non-pointer isa with weak refs and/or side table data.
clearDeallocating_slow();
}
assert(!sidetable_present());
}
- 当对象的isa没有经过优化,直接释放散列表,执行
sidetable_clearDeallocating()
;
void objc_object::sidetable_clearDeallocating(){
SideTable& table = SideTables()[this];
// clear any weak table items
// clear extra retain count and deallocating bit
// (fixme warn or abort if extra retain count == 0 ?)
table.lock();
//清空弱引用表
RefcountMap::iterator it = table.refcnts.find(this);
if (it != table.refcnts.end()) {
if (it->second & SIDE_TABLE_WEAKLY_REFERENCED) {
weak_clear_no_lock(&table.weak_table, (id)this);
}
table.refcnts.erase(it);
}
table.unlock();
}
- 当对象的isa经过优化,清空弱引用表和引用计数表,执行
clearDeallocating_slow()
;
NEVER_INLINE void objc_object::clearDeallocating_slow(){
ASSERT(isa.nonpointer && (isa.weakly_referenced || isa.has_sidetable_rc));
SideTable& table = SideTables()[this];
table.lock();
//清空弱引用表
if (isa.weakly_referenced) {
weak_clear_no_lock(&table.weak_table, (id)this);
}
//清空引用计数表
if (isa.has_sidetable_rc) {
table.refcnts.erase(this);
}
table.unlock();
}
-
weak_clear_no_lock
:清空弱引用表,实现如下:
void
weak_clear_no_lock(weak_table_t *weak_table, id referent_id)
{
objc_object *referent = (objc_object *)referent_id;
weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
if (entry == nil) {
/// XXX shouldn't happen, but does with mismatched CF/objc
//printf("XXX no entry for clear deallocating %p\n", referent);
return;
}
// zero out references
weak_referrer_t *referrers;
size_t count;
//referrers哈希表
if (entry->out_of_line()) {
referrers = entry->referrers;
count = TABLE_SIZE(entry);
} else {//inline_referrers数组
referrers = entry->inline_referrers;
count = WEAK_INLINE_COUNT;
}
//清空目标对象的所有weak指针
for (size_t i = 0; i < count; ++i) {
objc_object **referrer = referrers[i];
if (referrer) {
if (*referrer == referent) {
*referrer = nil;
}
else if (*referrer) {
_objc_inform("__weak variable at %p holds %p instead of %p. "
"This is probably incorrect use of "
"objc_storeWeak() and objc_loadWeak(). "
"Break on objc_weak_error to debug.\n",
referrer, (void*)*referrer, (void*)referent);
objc_weak_error();
}
}
}
//最后将weak_entry_t从weak_table_t中移除
weak_entry_remove(weak_table, entry);
}
retainCount源码分析
- (NSUInteger)retainCount {
return _objc_rootRetainCount(self);
}
- 内部调用
_objc_rootRetainCount
函数:
uintptr_t_objc_rootRetainCount(id obj){
ASSERT(obj);
return obj->rootRetainCount();
}
inline uintptr_t objc_object::rootRetainCount(){
if (isTaggedPointer()) return (uintptr_t)this;
sidetable_lock();
isa_t bits = LoadExclusive(&isa.bits);
ClearExclusive(&isa.bits);
//isa经过优化
if (bits.nonpointer) {
//获取isa位域的引用计数:extra_rc+1
uintptr_t rc = 1 + bits.extra_rc;
//获取散列表中的引用计数
if (bits.has_sidetable_rc) {
//两者相加
rc += sidetable_getExtraRC_nolock();
}
sidetable_unlock();
return rc;
}
sidetable_unlock();
//isa未经过优化,直接获取散列表中的引用计数
return sidetable_retainCount();
}
- 当对象的isa经过优化,首先获取isa位域extra_rc中的引用计数,默认会+1,
uintptr_t rc = 1 + bits.extra_rc
,然后获取散列表的引用计数表中的引用计数,两者相加得到对象的最终的引用计数,rc += sidetable_getExtraRC_nolock()
;
- 当对象的isa没有经过优化,直接获取散列表的引用计数表中的引用计数,返回;
- 当我们alloc一个对象时,然后调用retainCount函数,得到对象的引用计数为1,是因为在底层
rootRetainCount
方法中,引用计数默认+1了
,这里只有对引用计数的读取操作,是没有写入操作的,简单来说就是:为了防止alloc创建的对象被释放(引用计数为0会被释放),所以在编译阶段,程序底层默认进行了+1操作,实际上在extra_rc中的引用计数仍然为0;