當(dāng)對(duì)象的引用計(jì)數(shù)為0時(shí), 系統(tǒng)會(huì)調(diào)用對(duì)象的dealloc方法釋放
- (void)dealloc {
_objc_rootDealloc(self);
}
在內(nèi)部
void _objc_rootDealloc(id obj)
{
assert(obj);
obj->rootDealloc();
}
繼續(xù)調(diào)用了rootDealloc方法
顯然調(diào)用順序?yàn)?先調(diào)用當(dāng)前類的dealloc,然后調(diào)用父類的dealloc,最后到了NSObject的dealloc
inline void
objc_object::rootDealloc()
{
//判斷對(duì)象是否采用了Tagged Pointer技術(shù)
if (isTaggedPointer()) return; // fixme necessary?
//判斷是否能夠進(jìn)行快速釋放
//這里使用了isa指針里的屬性來進(jìn)行判斷.
if (fastpath(isa.nonpointer && //對(duì)象是否采用了優(yōu)化的isa計(jì)數(shù)方式
!isa.weakly_referenced && //對(duì)象沒有被弱引用
!isa.has_assoc && //對(duì)象沒有關(guān)聯(lián)對(duì)象
!isa.has_cxx_dtor && //對(duì)象沒有自定義的C++析構(gòu)函數(shù)
!isa.has_sidetable_rc //對(duì)象沒有用到sideTable來做引用計(jì)數(shù)
))
{
//如果以上判斷都符合條件,就會(huì)調(diào)用C函數(shù) ```free``` 將對(duì)象釋放
assert(!sidetable_present());
free(this);
}
else {
//如果以上判斷沒有通過,做下一步處理
object_dispose((id)this);
}
}
細(xì)數(shù)下判斷條件,fastpath封裝了編譯器指令,告訴編譯器內(nèi)部的條件大概率為真,減少調(diào)用時(shí)符號(hào)檢索的跳轉(zhuǎn)
對(duì)象采用了優(yōu)化的isa計(jì)數(shù)方式(isa.nonpointer)
對(duì)象沒有被weak引用!isa.weakly_referenced
沒有關(guān)聯(lián)對(duì)象!isa.has_assoc
沒有自定義的C++析構(gòu)方法!isa.has_cxx_dtor
沒有用到sideTable來做引用計(jì)數(shù) !isa.has_sidetable_rc
isa.nonpointer假
訪問對(duì)象的isa會(huì)直接返回一個(gè)指向cls的指針,是iPhone 遷移到64位系統(tǒng)之前時(shí)結(jié)構(gòu)isa.nonpointer真
代表是已經(jīng)優(yōu)化的isa,類的信息存儲(chǔ)在shiftcls中,64位架構(gòu)都是優(yōu)化過的
內(nèi)部做了一些判斷, 如果滿足這五個(gè)條件,直接調(diào)用free函數(shù),進(jìn)行內(nèi)存釋放.
當(dāng)一個(gè)最簡(jiǎn)單的類(沒有任何成員變量,沒有任何引用的類),這五個(gè)判斷條件都是成立的,直接free
id object_dispose(id obj)
{
if (!obj) return nil;
objc_destructInstance(obj);
free(obj);
return nil;
}
調(diào)用objc_destructInstance函數(shù)來析構(gòu)對(duì)象obj,再free(obj)釋放內(nèi)存.
objc_destructInstance內(nèi)部函數(shù)會(huì)銷毀C++析構(gòu)函數(shù)以及移除關(guān)聯(lián)對(duì)象的操作.
繼續(xù)調(diào)用objc_object的clearDeallocating函數(shù)做下一步處理
objc_object::clearDeallocating()
{
if (slowpath(!isa.nonpointer)) {
// Slow path for raw pointer isa.
// 如果要釋放的對(duì)象沒有采用了優(yōu)化過的isa引用計(jì)數(shù)
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.
// 如果要釋放的對(duì)象采用了優(yōu)化過的isa引用計(jì)數(shù),并且有弱引用或者使用了sideTable的輔助引用計(jì)數(shù)
clearDeallocating_slow();
}
assert(!sidetable_present());
}
根據(jù)是否采用了優(yōu)化過的isa做引用計(jì)數(shù)分為兩種:
- 要釋放的對(duì)象沒有采用優(yōu)化過的
isa引用計(jì)數(shù):
會(huì)調(diào)用sidetable_clearDeallocating()函數(shù)做進(jìn)一步處理
void objc_object::sidetable_clearDeallocating()
{
// 在全局的SideTables中,以this指針(要釋放的對(duì)象)為key,找到對(duì)應(yīng)的SideTable
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();
//在散列表SideTable中找到對(duì)應(yīng)的引用計(jì)數(shù)表RefcountMap,拿到要釋放的對(duì)象的引用計(jì)數(shù)
RefcountMap::iterator it = table.refcnts.find(this);
if (it != table.refcnts.end()) {
//如果要釋放的對(duì)象被弱引用了,通過weak_clear_no_lock函數(shù)將指向該對(duì)象的弱引用指針置為nil
if (it->second & SIDE_TABLE_WEAKLY_REFERENCED) {
weak_clear_no_lock(&table.weak_table, (id)this);
}
//從引用計(jì)數(shù)表中擦除該對(duì)象的引用計(jì)數(shù)
table.refcnts.erase(it);
}
table.unlock();
}
2.如果該對(duì)象采用了優(yōu)化過的isa引用計(jì)數(shù)
并且該對(duì)象有弱引用或者使用了sideTable的輔助引用計(jì)數(shù),就會(huì)調(diào)用clearDeallocating_slow()函數(shù)做進(jìn)一步處理
NEVER_INLINE void
objc_object::clearDeallocating_slow()
{
assert(isa.nonpointer && (isa.weakly_referenced || isa.has_sidetable_rc));
// 在全局的SideTables中,以this指針(要釋放的對(duì)象)為key,找到對(duì)應(yīng)的SideTable
SideTable& table = SideTables()[this];
table.lock();
if (isa.weakly_referenced) {
//要釋放的對(duì)象被弱引用了,通過weak_clear_no_lock函數(shù)將指向該對(duì)象的弱引用指針置為nil
weak_clear_no_lock(&table.weak_table, (id)this);
}
//使用了sideTable的輔助引用計(jì)數(shù),直接在SideTable中擦除該對(duì)象的引用計(jì)數(shù)
if (isa.has_sidetable_rc) {
table.refcnts.erase(this);
}
table.unlock();
}
以上兩種情況都涉及weak_clear_no_lock函數(shù), 它的作用就是將被弱引用對(duì)象的弱引用指針置為nil.
void weak_clear_no_lock(weak_table_t *weak_table, id referent_id)
{
//獲取被弱引用對(duì)象的地址
objc_object *referent = (objc_object *)referent_id;
// 根據(jù)對(duì)象地址找到被弱引用對(duì)象referent在weak_table中對(duì)應(yīng)的weak_entry_t
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;
// 找出弱引用該對(duì)象的所有weak指針地址數(shù)組
if (entry->out_of_line()) {
referrers = entry->referrers;
count = TABLE_SIZE(entry);
}
else {
referrers = entry->inline_referrers;
count = WEAK_INLINE_COUNT;
}
// 遍歷取出每個(gè)weak指針的地址
for (size_t i = 0; i < count; ++i) {
objc_object **referrer = referrers[i];
if (referrer) {
// 如果weak指針確實(shí)弱引用了對(duì)象 referent,則將weak指針設(shè)置為nil
if (*referrer == referent) {
*referrer = nil;
}
// 如果所存儲(chǔ)的weak指針沒有弱引用對(duì)象 referent,這可能是由于runtime代碼的邏輯錯(cuò)誤引起的,報(bào)錯(cuò)
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_remove(weak_table, entry);
}
這里也表明了為什么被weak修飾的對(duì)象在釋放時(shí), 所有弱引用該對(duì)象的指針都被設(shè)置為nil.
dealloc整個(gè)方法釋放流程如下圖:

看流程圖發(fā)現(xiàn),如果五個(gè)條件不滿足.內(nèi)存無法進(jìn)行快速釋放.在上面中,我看到博客里關(guān)于objc_destructInstance 這個(gè)方法只是概述而過,所以我找了相關(guān)資料來了解一下.
void *objc_destructInstance(id obj)
{
if (obj) {
Class isa_gen = _object_getClass(obj);
class_t *isa = newcls(isa_gen);
// Read all of the flags at once for performance.
bool cxx = hasCxxStructors(isa);
bool assoc = !UseGC && _class_instancesHaveAssociatedObjects(isa_gen);
// This order is important.
if (cxx) object_cxxDestruct(obj);
if (assoc) _object_remove_assocations(obj);
if (!UseGC) objc_clear_deallocating(obj);
}
return obj;
}
總共干了三件事::
- 執(zhí)行了
object_cxxDestruct函數(shù) - 執(zhí)行
_object_remove_assocations,去除了關(guān)聯(lián)對(duì)象.(這也是為什么category添加屬性時(shí),在釋放時(shí)沒有必要remove) - 就是上面寫的那個(gè),清空引用計(jì)數(shù)表并清除弱引用表,將
weak指針置為nil
object_cxxDestruct是由編譯器生成,這個(gè)方法原本是為了C++對(duì)象析構(gòu),ARC借用了這個(gè)方法插入代碼實(shí)現(xiàn)了自動(dòng)內(nèi)存釋放的工作.
這個(gè)釋放現(xiàn)象: - 當(dāng)類擁有實(shí)例變量時(shí),這個(gè)方法會(huì)出現(xiàn),且父類的實(shí)例變量不會(huì)導(dǎo)致子類擁有這個(gè)方法.
- 出現(xiàn)這個(gè)方法和變量是否被賦值,賦值成什么沒有關(guān)系.
所以, 我們可以認(rèn)為這個(gè)方法就是用來釋放該類中的屬性的. weak修飾的屬性應(yīng)該不包含在內(nèi).
總結(jié)
對(duì)象的引用計(jì)數(shù)為0時(shí)會(huì)執(zhí)行dealloc函數(shù),調(diào)用棧如下:
dealloc->_objc_rootDealloc->object_dispose->objc_destructInstance
objc_destructInstance函數(shù)內(nèi)部會(huì)依次調(diào)用c++析構(gòu)函數(shù)object_cxxDestruct、關(guān)聯(lián)對(duì)象析構(gòu)函數(shù)_object_remove_assocations、弱引用析構(gòu)函數(shù)clearDeallocating