本文主要是在源碼層面上分析內(nèi)存管理的引用計(jì)數(shù)的變化過程以及。包括alloc/retain/release/dealloc/retainCount的源碼分析
主要內(nèi)容:
1、引用計(jì)數(shù)的過程
2、弱引用表的結(jié)構(gòu)
3、sideTable散列表的結(jié)構(gòu)
retain的分析
retain
在源碼中全局搜索retain()
源碼:
// Equivalent to calling [this retain], with shortcuts if there is no override
//相當(dāng)于調(diào)用[this retain],如果沒有重寫,則使用快捷方式
inline id
objc_object::retain()
{
ASSERT(!isTaggedPointer());
//如果沒有重寫retain方法,就會(huì)執(zhí)行默認(rèn)的
if (fastpath(!ISA()->hasCustomRR())) {
return rootRetain();
}
//如果本類已經(jīng)重寫了,就和普通的方法調(diào)用一樣直接調(diào)用重寫的方法即可
return ((id(*)(objc_object *, SEL))objc_msgSend)(this, @selector(retain));
}
說明:
- 如果我們重寫了retain方法,會(huì)進(jìn)行消息發(fā)送來(lái)調(diào)用
- 如果沒有重寫,就直接調(diào)用已有的rootRetain函數(shù)
- 因此我們平時(shí)要重寫retain方法就需要[super retain]
rootRetain
通過retain -> rootRetain() -> rootRetain(bool tryRetain, bool handleOverflow),找到rootRetain函數(shù)進(jìn)行分析
源碼:
/*
進(jìn)行一次reatain真正進(jìn)行的操作
過程:
1、如果不是nonpointr_isa,就直接操作散列表
2、如果正在釋放,就直接dealloc
3、引用計(jì)數(shù)+1
4、判斷如果carry已經(jīng)滿了,就將引用計(jì)數(shù)的一半放到散列表中,一半放到extra_rc中
*/
ALWAYS_INLINE id
objc_object::rootRetain(bool tryRetain, bool handleOverflow)
{
if (isTaggedPointer()) return (id)this;//小對(duì)象類型直接返回,不進(jìn)行任何操作
bool sideTableLocked = false;
bool transcribeToSideTable = false;//轉(zhuǎn)移到sideTable
//這里使用isa是因?yàn)閕sa中存儲(chǔ)有引用計(jì)數(shù)
isa_t oldisa;
isa_t newisa;
do {
transcribeToSideTable = false;
oldisa = LoadExclusive(&isa.bits);
newisa = oldisa;
//判斷是否為nonpointer isa
if (slowpath(!newisa.nonpointer)) {
//如果不是 nonpointer isa,直接操作散列表sidetable
ClearExclusive(&isa.bits);
if (rawISA()->isMetaClass()) return (id)this;//如果是元類,直接返回當(dāng)前對(duì)象
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
//是否正在釋放,直接dealloc
if (slowpath(tryRetain && newisa.deallocating)) {
ClearExclusive(&isa.bits);
if (!tryRetain && sideTableLocked) sidetable_unlock();
return nil;
}
uintptr_t carry;
//執(zhí)行引用計(jì)數(shù)+1操作,即對(duì)bits中的extra_rc進(jìn)行+1操作,通過1ULL<<45(arm64,用于該對(duì)象存儲(chǔ)引用計(jì)數(shù)值
//狀態(tài)標(biāo)識(shí)carry,用于表示extra_rc是否滿了
newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry); // extra_rc++
//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.
//這里只將一半存入到extra_rc中,剩下的一半存儲(chǔ)到散列表中
if (!tryRetain && !sideTableLocked) sidetable_lock();
sideTableLocked = true;
transcribeToSideTable = true;
//給isa的extra_rc設(shè)置數(shù)值
newisa.extra_rc = RC_HALF;
//給isa的has_sidetable_rc設(shè)置標(biāo)識(shí)符為YES,表示在散列表中也存儲(chǔ)有數(shù)據(jù)
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.
//復(fù)制另一個(gè)半到散列表中
sidetable_addExtraRC_nolock(RC_HALF);
}
if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock();
return (id)this;
}

isa存儲(chǔ)情況.png
代碼結(jié)構(gòu):
- 判斷如果是小對(duì)象類型,不進(jìn)行retain,直接返回
- 判斷如果不是nonpointer,就直接操作散列表
- 如果是元類就直接返回,元類無(wú)需進(jìn)行引用計(jì)數(shù)的計(jì)算(前面我們分析過,元類不是nonpointer_isa)
- 如果不是元類,直接操作散列表sidetable
- 判斷如果該對(duì)象正在dealloc,不作任何操作直接退出
- 對(duì)isa中的extra_rc進(jìn)行+1操作,并且通過carry存儲(chǔ)是否已滿
- 如果extra_rc已經(jīng)達(dá)到最大值,數(shù)據(jù)已滿,carry就是YES
- 當(dāng)carry為YES,就將extra_rc中的一半取出來(lái)賦值到isa中的extra_rc,另一半拿出來(lái)存儲(chǔ)到sidetable的計(jì)數(shù)表中。并且設(shè)置isa中的has_sidetable_rc為YES,表示散列表中存儲(chǔ)有引用計(jì)數(shù)。
說明:
- 不是nonpointer_isa就只有散列表存儲(chǔ)引用計(jì)數(shù),這也很容易理解,因?yàn)榉莕onpointer的isa只存儲(chǔ)有類信息,沒有其他信息。無(wú)法存儲(chǔ)引用計(jì)數(shù)
- nonpointer_isa有兩個(gè)值,一個(gè)是extra_rc,一個(gè)是has_sidetable_rc,分別用來(lái)存儲(chǔ)引用計(jì)數(shù)和是否存儲(chǔ)有sidetable。
- 如果extra_rc沒有滿,引用計(jì)數(shù)只存儲(chǔ)在extar_rc(arm占有19位)
- 如果extra_rc已經(jīng)滿了,就需要取出數(shù)據(jù)的一半存儲(chǔ)在sidetable。并且將isa中的has_sidetable_rc設(shè)置為YES(has_sidetable_rc占有兩位)
retain的過程:
- 先給isa中的extra_rc引用計(jì)數(shù)+1
- 如果extra_rc溢出,就取出一半放到sideTable中的引用計(jì)數(shù)表中。只留一半在extra_rc中
- 如果正在dealloc,就直接dealloc不進(jìn)行任何操作
sidetable的認(rèn)識(shí):
源碼:
// RefcountMap disguises its pointers because we
// don't want the table to act as a root for `leaks`.
//這個(gè)引用計(jì)數(shù)表是對(duì)象作為鍵,引用計(jì)數(shù)作為值來(lái)存儲(chǔ)的
//當(dāng)引用計(jì)數(shù)為0時(shí),會(huì)自動(dòng)將其objc刪除掉
typedef objc::DenseMap<DisguisedPtr<objc_object>,size_t,RefcountMapValuePurgeable> RefcountMap;
// Template parameters.
enum HaveOld { DontHaveOld = false, DoHaveOld = true };
enum HaveNew { DontHaveNew = false, DoHaveNew = true };
//額外的散列表
struct SideTable {
spinlock_t slock;//自旋鎖,對(duì)于表的使用需要加解鎖
RefcountMap refcnts;//引用計(jì)數(shù)表,僅在未開啟isa優(yōu)化 或 在isa優(yōu)化情況下isa_t的引用計(jì)數(shù)溢出時(shí)才會(huì)用到
weak_table_t weak_table;//弱引用表,弱引用計(jì)數(shù)存儲(chǔ)在這里
SideTable() {
memset(&weak_table, 0, sizeof(weak_table));
}
//可以看到SideTable是不可以析構(gòu)的,
~SideTable() {
_objc_fatal("Do not delete SideTable.");
}
//鎖的操作,SideTable的讀取需要進(jìn)行加解鎖
void lock() { slock.lock(); }
void unlock() { slock.unlock(); }
void forceReset() { slock.forceReset(); }
// Address-ordered lock discipline for a pair of side tables.
template<HaveOld, HaveNew>
static void lockTwo(SideTable *lock1, SideTable *lock2);
template<HaveOld, HaveNew>
static void unlockTwo(SideTable *lock1, SideTable *lock2);
};
說明:
- 額外信息表SideTable存儲(chǔ)有引用計(jì)數(shù)表和弱引用表
- 引用計(jì)數(shù)表僅在未開啟isa優(yōu)化 或 在isa優(yōu)化情況下isa_t的引用計(jì)數(shù)溢出時(shí)才會(huì)用到
- 這個(gè)引用計(jì)數(shù)表是對(duì)象作為鍵,引用計(jì)數(shù)作為值來(lái)存儲(chǔ)的
- 當(dāng)一個(gè)對(duì)象被弱引用時(shí),就需要在弱引用表中進(jìn)行存儲(chǔ)
- 同時(shí)在這里還有一個(gè)自旋鎖,是通過spinlock_t來(lái)實(shí)現(xiàn)的
- 自旋鎖簡(jiǎn)單來(lái)說會(huì)忙等待,效率高。
- 加鎖是為了線程安全,防止對(duì)引用計(jì)數(shù)的讀取出現(xiàn)差錯(cuò)。
- 同時(shí)看到SideTable是不可以刪除的,這個(gè)是系統(tǒng)自己創(chuàng)建的,我們無(wú)法刪除
weak_table_t
源碼:
/**
* The global weak references table. Stores object ids as keys,
* and weak_entry_t structs as their values.
* 全局弱引用表。
* 將對(duì)象ID存儲(chǔ)為鍵,將weak_entry_t結(jié)構(gòu)體存儲(chǔ)為值
*/
struct weak_table_t {
weak_entry_t *weak_entries;//是一個(gè)動(dòng)態(tài)數(shù)組,里面存儲(chǔ)了很多的弱引用
size_t num_entries;//弱引用的數(shù)量
uintptr_t mask;//用來(lái)哈希計(jì)算,為總數(shù)-1
uintptr_t max_hash_displacement;//可能發(fā)生的最大沖突數(shù)
};
說明:
- 可以看到weak_table_t這是一個(gè)全局的弱引用表,里面存儲(chǔ)有多個(gè)弱引用表
- 在weak_entries中對(duì)象ID作為鍵,weak_entry_t作為值存儲(chǔ)。
總結(jié):
- 弱引用表存在于sideTable表中
- isa中有weakly_refrenced來(lái)判斷是否存在弱引用
- 弱引用表使用對(duì)象ID作為鍵,弱引用動(dòng)態(tài)數(shù)組作為值存儲(chǔ)
release
通過objc_release() -> release() -> rootRelease() -> rootRelease(bool performDealloc, bool handleUnderflow)查找到是在rootRelease函數(shù)中進(jìn)行了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)) {//如果不是nonpointer_isa,就直接操作sideTable,進(jìn)行減一
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
//進(jìn)行引用計(jì)數(shù)-1操作,即extra_rc-1
uintptr_t carry;
newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry);
//如果此時(shí)extra_rc的值為0了,則走到underflow,
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
// 如果isa中的extra_rc減完之后已經(jīng)為0,就開始從sideTable獲取數(shù)據(jù),或者如果全部為0,就開始deallocate
// abandon newisa to undo the decrement
newisa = oldisa;
//通過has_sidetable_rc判斷散列表中是否存在計(jì)數(shù)
if (slowpath(newisa.has_sidetable_rc)) {
if (!handleUnderflow) {
ClearExclusive(&isa.bits);
return rootRelease_underflow(performDealloc);
}
// Transfer retain count from side table to inline storage.
//從sideTable轉(zhuǎn)移引用計(jì)數(shù)到extra_rc
//先上鎖
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.
//取出一半的引用計(jì)數(shù)
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.
//即使已經(jīng)為0了,也需要設(shè)置has_sidetable_rc
if (borrowed > 0) {
// Side table retain count decreased.
// Try to add them to the inline count.
//extra_rc有數(shù)據(jù)后,就再-1
newisa.extra_rc = borrowed - 1; // redo the original decrement too,計(jì)數(shù)-1
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.
}
}
// Really deallocate.
//如果沒有就直接dealloc
if (slowpath(newisa.deallocating)) {
ClearExclusive(&isa.bits);
if (sideTableLocked) sidetable_unlock();
return overrelease_error();
// does not actually return
}
newisa.deallocating = true;
if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;
if (slowpath(sideTableLocked)) sidetable_unlock();
__c11_atomic_thread_fence(__ATOMIC_ACQUIRE);
if (performDealloc) {
((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(dealloc));
}
return true;
}
說明:
- 過程與retain的剛好相反
- 先直接將isa中的extra_rc中的數(shù)據(jù)進(jìn)行-1
- 如果已經(jīng)為0,則從sideTable獲取數(shù)據(jù)
- 如果sideTable中也沒有數(shù)據(jù),就進(jìn)行dealloc
- 如果有數(shù)據(jù),就取出一半的數(shù)據(jù)放到extra_rc中,再對(duì)extra_rc進(jìn)行-1
release的過程為:
- 如果isa中的extra_rc引用計(jì)數(shù)值不為0,就直接減1;
- 如果值為0,就先從sideTable的引用計(jì)數(shù)獲取值,取出其中的一半存儲(chǔ)到extra_rc中,并繼續(xù)-1.
- 如果減完之后引用計(jì)數(shù)為0,就開始dealloc。
dealloc
在源碼中通過dealloc -> _objc_rootDealloc -> rootDealloc,在rootDealloc中進(jìn)行真正的dealloc操作
rootDealloc
源碼:
inline void
objc_object::rootDealloc()
{
if (isTaggedPointer()) return; // fixme necessary?
//如果是nonpointer、而且有弱引用,關(guān)聯(lián)對(duì)象、是否有析構(gòu)器,是否有存儲(chǔ)的引用計(jì)數(shù)
/*
如果是nonpointer_isa,不會(huì)進(jìn)
如果不是nonpointer_isa,這幾個(gè)條件有一個(gè)還在,就不會(huì)進(jìn)
*/
if (fastpath(isa.nonpointer &&
!isa.weakly_referenced &&
!isa.has_assoc &&
!isa.has_cxx_dtor &&
!isa.has_sidetable_rc))
{
assert(!sidetable_present());
free(this);
}
//如果非nonpointer_isa或者是nonpointer_isa,且存在那幾個(gè)條件就需要銷毀一些東西
else {
object_dispose((id)this);
}
}
說明:
- 如果是小對(duì)象類型無(wú)需操作
- 如果是nonpointer_isa,但是不存在弱引用表、關(guān)聯(lián)對(duì)象、析構(gòu)器、額外數(shù)據(jù)表sidetable,就可以直接刪掉
- 如果是非nonpointer_isa,或者是nonpointer_isa,但是存在弱引用表、關(guān)聯(lián)對(duì)象、析構(gòu)器、額外數(shù)據(jù)表sidetable,就需要先刪除這些東西。
object_dispose
源碼:
id
object_dispose(id obj)
{
if (!obj) return nil;
//先破壞對(duì)象
objc_destructInstance(obj);
//再清除空間
free(obj);
return nil;
}
說明:
- 這里只是調(diào)用objc_destructInstance進(jìn)行銷毀對(duì)象,之后再清除對(duì)象的空間
objc_destructInstance
源碼:
/***********************************************************************
* objc_destructInstance
* Destroys an instance without freeing memory. 這里只銷毀實(shí)例,不在這里清除內(nèi)存
* Calls C++ destructors.調(diào)用C++析構(gòu)器
* Calls ARC ivar cleanup.調(diào)用ARC變量清除
* Removes associative references.移除關(guān)聯(lián)對(duì)象
* Returns `obj`. Does nothing if `obj` is nil.將返回obj,如果obj是nil,將不進(jìn)行任何操作
**********************************************************************/
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.
//這個(gè)順序很重要。
/*
1、先進(jìn)行C++析構(gòu)函數(shù)調(diào)用
2、移除關(guān)聯(lián)對(duì)象
3、弱引用
4、sideTable
*/
/*
如果是nonapoint_isa,,不移除弱引用
*/
if (cxx) object_cxxDestruct(obj);//C++析構(gòu)函數(shù)
if (assoc) _object_remove_assocations(obj);//刪除關(guān)聯(lián)對(duì)象
obj->clearDeallocating();
}
return obj;
}
clearDeallocating
源碼:
/*
刪除has_sidetable_rc和extra_rc
*/
inline void
objc_object::clearDeallocating()
{
//如果不是nonpointer,清除has_sidetable_rc和extra_rc
if (slowpath(!isa.nonpointer)) {
// Slow path for raw pointer isa.
sidetable_clearDeallocating();
}
//判斷是否存在弱引用和額外的引用計(jì)數(shù)
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());
}
說明:
- 這里只銷毀,不清除內(nèi)存
- 一個(gè)是注意銷毀的內(nèi)容包括C++析構(gòu)函數(shù)、關(guān)聯(lián)對(duì)象、弱引用、sideTable。
- 第二個(gè)是注意銷毀的順序?yàn)镃++析構(gòu)函數(shù)、關(guān)聯(lián)對(duì)象、弱引用、sideTable。
總結(jié):
- dealloc時(shí)需要先清除C++析構(gòu)函數(shù)、關(guān)聯(lián)對(duì)象、弱引用、sideTable,通過isa來(lái)判斷是否需要清除
- 清除的順序?yàn)镃++析構(gòu)函數(shù)、關(guān)聯(lián)對(duì)象、弱引用、sideTable
retainCount
源碼:
inline uintptr_t
objc_object::rootRetainCount()
{
if (isTaggedPointer()) return (uintptr_t)this;//小對(duì)象類型不用處理
sidetable_lock();
isa_t bits = __c11_atomic_load((_Atomic uintptr_t *)&isa.bits, __ATOMIC_RELAXED);
//如果是nonpointer,取出extra_rc和sidetable中的引用計(jì)數(shù)加在一起取出來(lái)
if (bits.nonpointer) {
uintptr_t rc = bits.extra_rc;
if (bits.has_sidetable_rc) {
rc += sidetable_getExtraRC_nolock();
}
sidetable_unlock();
return rc;
}
sidetable_unlock();
return sidetable_retainCount();
}
說明:
- extra_rc和sidetable中的引用計(jì)數(shù)加在一起就是這個(gè)對(duì)象的引用計(jì)數(shù)值
看一個(gè)面試題打印什么?
- (void)test2{
NSLog(@"%ld",CFGetRetainCount((__bridge CFTypeRef)[NSObject alloc]));
}
說明:
- 查看運(yùn)行結(jié)果打印的是1。
- 此處我們并沒有將這個(gè)對(duì)象附給任何變量,但是就已經(jīng)引用計(jì)數(shù)+1了。
初始化isa源碼
inline void
objc_object::initIsa(Class cls, bool nonpointer, UNUSED_WITHOUT_INDEXED_ISA_AND_DTOR_BIT bool hasCxxDtor)
{
ASSERT(!isTaggedPointer());
isa_t newisa(0);
if (!nonpointer) {
newisa.setClass(cls, this);
} else {
ASSERT(!DisableNonpointerIsa);
ASSERT(!cls->instancesRequireRawIsa());
#if SUPPORT_INDEXED_ISA
ASSERT(cls->classArrayIndex() > 0);
newisa.bits = ISA_INDEX_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = hasCxxDtor;
newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
newisa.bits = ISA_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
# if ISA_HAS_CXX_DTOR_BIT
newisa.has_cxx_dtor = hasCxxDtor;
# endif
newisa.setClass(cls, this);
#endif
newisa.extra_rc = 1;
}
// This write must be performed in a single store in some cases
// (for example when realizing a class because other threads
// may simultaneously try to use the class).
// fixme use atomics here to guarantee single-store and to
// guarantee memory order w.r.t. the class index table
// ...but not too atomic because we don't want to hurt instantiation
isa = newisa;
}
說明:
- 其他的以前均已經(jīng)分析過,這里單看newisa.extra_rc = 1;這一條語(yǔ)句,在初始化isa的時(shí)候就已經(jīng)給extra_rc設(shè)置為1。
- 這也就是當(dāng)alloc一個(gè)對(duì)象之后引用計(jì)數(shù)就為1.
總結(jié):
- retainCount得到的值是isa中的extra_rc中的值加上sideTable中的引用計(jì)數(shù)的值
- 當(dāng)alloc創(chuàng)建出對(duì)象時(shí),默認(rèn)引用計(jì)數(shù)為1