當(dāng)對象的引用計(jì)數(shù)為0時(shí), 系統(tǒng)會(huì)調(diào)用對象的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()
{
//判斷對象是否采用了Tagged Pointer技術(shù)
if (isTaggedPointer()) return; // fixme necessary?
//判斷是否能夠進(jìn)行快速釋放
//這里使用了isa指針里的屬性來進(jìn)行判斷.
if (fastpath(isa.nonpointer && //對象是否采用了優(yōu)化的isa計(jì)數(shù)方式
!isa.weakly_referenced && //對象沒有被弱引用
!isa.has_assoc && //對象沒有關(guān)聯(lián)對象
!isa.has_cxx_dtor && //對象沒有自定義的C++析構(gòu)函數(shù)
!isa.has_sidetable_rc //對象沒有用到sideTable來做引用計(jì)數(shù)
))
{
//如果以上判斷都符合條件,就會(huì)調(diào)用C函數(shù) free 將對象釋放
assert(!sidetable_present());
free(this);
}
else {
//如果以上判斷沒有通過,做下一步處理
object_dispose((id)this);
}
}
內(nèi)部做了一些判斷, 如果滿足這五個(gè)條件,直接調(diào)用free函數(shù),進(jìn)行內(nèi)存釋放.
當(dāng)一個(gè)最簡單的類(沒有任何成員變量,沒有任何引用的類),這五個(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)對象obj,再free(obj)釋放內(nèi)存.
objc_destructInstance內(nèi)部函數(shù)會(huì)銷毀C++析構(gòu)函數(shù)以及移除關(guān)聯(lián)對象的操作.
繼續(xù)調(diào)用objc_object的clearDeallocating函數(shù)做下一步處理
objc_object::clearDeallocating()
{
if (slowpath(!isa.nonpointer)) {
// Slow path for raw pointer isa.
// 如果要釋放的對象沒有采用了優(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.
// 如果要釋放的對象采用了優(yōu)化過的isa引用計(jì)數(shù),并且有弱引用或者使用了sideTable的輔助引用計(jì)數(shù)
clearDeallocating_slow();
}
assert(!sidetable_present());
}
根據(jù)是否采用了優(yōu)化過的isa做引用計(jì)數(shù)分為兩種:
- 要釋放的對象沒有采用優(yōu)化過的isa引用計(jì)數(shù):
會(huì)調(diào)用sidetable_clearDeallocating() 函數(shù)做進(jìn)一步處理
void
objc_object::sidetable_clearDeallocating()
{
// 在全局的SideTables中,以this指針(要釋放的對象)為key,找到對應(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中找到對應(yīng)的引用計(jì)數(shù)表RefcountMap,拿到要釋放的對象的引用計(jì)數(shù)
RefcountMap::iterator it = table.refcnts.find(this);
if (it != table.refcnts.end()) {
//如果要釋放的對象被弱引用了,通過weak_clear_no_lock函數(shù)將指向該對象的弱引用指針置為nil
if (it->second & SIDE_TABLE_WEAKLY_REFERENCED) {
weak_clear_no_lock(&table.weak_table, (id)this);
}
//從引用計(jì)數(shù)表中擦除該對象的引用計(jì)數(shù)
table.refcnts.erase(it);
}
table.unlock();
}
- 如果該對象采用了優(yōu)化過的isa引用計(jì)數(shù)
并且該對象有弱引用或者使用了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指針(要釋放的對象)為key,找到對應(yīng)的SideTable
SideTable& table = SideTables()[this];
table.lock();
if (isa.weakly_referenced) {
//要釋放的對象被弱引用了,通過weak_clear_no_lock函數(shù)將指向該對象的弱引用指針置為nil
weak_clear_no_lock(&table.weak_table, (id)this);
}
//使用了sideTable的輔助引用計(jì)數(shù),直接在SideTable中擦除該對象的引用計(jì)數(shù)
if (isa.has_sidetable_rc) {
table.refcnts.erase(this);
}
table.unlock();
}
以上兩種情況都涉及weak_clear_no_lock函數(shù), 它的作用就是將被弱引用對象的弱引用指針置為nil.
void
weak_clear_no_lock(weak_table_t *weak_table, id referent_id)
{
//獲取被弱引用對象的地址
objc_object *referent = (objc_object *)referent_id;
// 根據(jù)對象地址找到被弱引用對象referent在weak_table中對應(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;
// 找出弱引用該對象的所有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í)弱引用了對象 referent,則將weak指針設(shè)置為nil
if (*referrer == referent) {
*referrer = nil;
}
// 如果所存儲的weak指針沒有弱引用對象 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修飾的對象在釋放時(shí), 所有弱引用該對象的指針都被設(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)對象.(這也是為什么category添加屬性時(shí),在釋放時(shí)沒有必要remove)
- 就是上面寫的那個(gè),清空引用計(jì)數(shù)表并清除弱引用表,將weak指針置為nil
object_cxxDestruct是由編譯器生成,這個(gè)方法原本是為了++對象析構(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)。