一节仿、weak_table_t
和 weak_entry_t
的結(jié)構(gòu)
源碼:
/*
The weak table is a hash table governed by a single spin lock.
An allocated blob of memory, most often an object, but under GC any such
allocation, may have its address stored in a __weak marked storage location
through use of compiler generated write-barriers or hand coded uses of the
register weak primitive. Associated with the registration can be a callback
block for the case when one of the allocated chunks of memory is reclaimed.
The table is hashed on the address of the allocated memory. When __weak
marked memory changes its reference, we count on the fact that we can still
see its previous reference.
So, in the hash table, indexed by the weakly referenced item, is a list of
all locations where this address is currently being stored.
For ARC, we also keep track of whether an arbitrary object is being
deallocated by briefly placing it in the table just prior to invoking
dealloc, and removing it via objc_clear_deallocating just prior to memory
reclamation.
*/
// The address of a __weak variable.
// These pointers are stored disguised so memory analysis tools
// don't see lots of interior pointers from the weak table into objects.
typedef DisguisedPtr<objc_object *> weak_referrer_t;
#if __LP64__
#define PTR_MINUS_2 62
#else
#define PTR_MINUS_2 30
#endif
/**
* The internal structure stored in the weak references table.
* It maintains and stores
* a hash set of weak references pointing to an object.
* If out_of_line_ness != REFERRERS_OUT_OF_LINE then the set
* is instead a small inline array.
*/
#define WEAK_INLINE_COUNT 4
// out_of_line_ness field overlaps with the low two bits of inline_referrers[1].
// inline_referrers[1] is a DisguisedPtr of a pointer-aligned address.
// The low two bits of a pointer-aligned DisguisedPtr will always be 0b00
// (disguised nil or 0x80..00) or 0b11 (any other address).
// Therefore out_of_line_ness == 0b10 is used to mark the out-of-line state.
#define REFERRERS_OUT_OF_LINE 2
struct weak_entry_t {
DisguisedPtr<objc_object> referent;
union {
struct {
weak_referrer_t *referrers;
uintptr_t out_of_line_ness : 2;
uintptr_t num_refs : PTR_MINUS_2;
uintptr_t mask;
uintptr_t max_hash_displacement;
};
struct {
// out_of_line_ness field is low bits of inline_referrers[1]
weak_referrer_t inline_referrers[WEAK_INLINE_COUNT];
};
};
bool out_of_line() {
return (out_of_line_ness == REFERRERS_OUT_OF_LINE);
}
weak_entry_t& operator=(const weak_entry_t& other) {
memcpy(this, &other, sizeof(other));
return *this;
}
weak_entry_t(objc_object *newReferent, objc_object **newReferrer)
: referent(newReferent)
{
inline_referrers[0] = newReferrer;
for (int i = 1; i < WEAK_INLINE_COUNT; i++) {
inline_referrers[i] = nil;
}
}
};
/**
* The global weak references table. Stores object ids as keys,
* and weak_entry_t structs as their values.
*/
struct weak_table_t {
weak_entry_t *weak_entries;
size_t num_entries;
uintptr_t mask;
uintptr_t max_hash_displacement;
};
/// Adds an (object, weak pointer) pair to the weak table.
id weak_register_no_lock(weak_table_t *weak_table, id referent,
id *referrer, bool crashIfDeallocating);
/// Removes an (object, weak pointer) pair from the weak table.
void weak_unregister_no_lock(weak_table_t *weak_table, id referent, id *referrer);
#if DEBUG
/// Returns true if an object is weakly referenced somewhere.
bool weak_is_registered_no_lock(weak_table_t *weak_table, id referent);
#endif
/// Called on object destruction. Sets all remaining weak pointers to nil.
void weak_clear_no_lock(weak_table_t *weak_table, id referent);
__END_DECLS
#endif /* _OBJC_WEAK_H_ */
下面依次開始分析 對(duì)象的weak
引用 和 釋放遇绞。
二除呵、weak
原理分析
調(diào)試所用代碼:
- (void)my_weak {
__weak typeof(self)weakSelf = self;
self.block = ^ {
__strong typeof(weakSelf)strongSelf = weakSelf;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",strongSelf.name);
});
};
}
1、weak
引用 - 源碼分析
clang
編譯轧坎,報(bào)錯(cuò)如下:
cannot create __weak
reference because the current deployment target does not support weak references
__attribute__((objc_ownership(weak))) typeof(self)weakSelf = self
編譯時(shí)不能創(chuàng)建__weak
引用宏邮,不支持;__weak
是運(yùn)行時(shí)runtime
初始化并維護(hù)的缸血。
運(yùn)行工程蜜氨,通過匯編斷點(diǎn)調(diào)試可知源碼在libobjc.A.dylib
.
libobjc.A.dylib`objc_initWeak:
1.1、初始化 - objc_initWeak()
objc_initWeak()
源碼:
/**
* Initialize a fresh weak pointer to some object location.
* It would be used for code like:
*
* (The nil case)
* __weak id weakPtr;
* (The non-nil case)
* NSObject *o = ...;
* __weak id weakPtr = o;
*
* This function IS NOT thread-safe with respect to concurrent
* modifications to the weak variable. (Concurrent weak clear is safe.)
*
* @param location Address of __weak ptr. // __weak指針的地址
* @param newObj Object ptr. // 對(duì)象ptr
*/
id
objc_initWeak(id *location, id newObj)
{
if (!newObj) {// 沒有 objc
*location = nil;
return nil;
}
//
return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
(location, (objc_object*)newObj);
}
DontHaveOld
/ DoHaveNew
/ DoCrashIfDeallocating
:
// Template parameters.
enum HaveOld { DontHaveOld = false, DoHaveOld = true };
enum HaveNew { DontHaveNew = false, DoHaveNew = true };
1.2捎泻、更新弱變量 - storeWeak()
storeWeak()
源碼:
// Update a weak variable.
// If HaveOld is true, the variable has an existing value
// that needs to be cleaned up. This value might be nil.
// If HaveNew is true, there is a new value that needs to be
// assigned into the variable. This value might be nil.
// If CrashIfDeallocating is true, the process is halted if newObj is
// deallocating or newObj's class does not support weak references.
// If CrashIfDeallocating is false, nil is stored instead.
/**
當(dāng)CrashIfDeallocating 為 true 時(shí)飒炎,如果newObj正在進(jìn)行deallocating或 newObj的類不支持弱引用,進(jìn)程將被停止笆豁;
當(dāng)CrashIfDeallocating 為 false 時(shí)郎汪,則存儲(chǔ) nil.
*/
enum CrashIfDeallocating {
DontCrashIfDeallocating = false, DoCrashIfDeallocating = true
};
template <HaveOld haveOld, HaveNew haveNew,
CrashIfDeallocating crashIfDeallocating>
static id
storeWeak(id *location, objc_object *newObj)
{
// initWeak 時(shí):haveOld = NO, haveNew = YES, crashIfDeallocating = YES
ASSERT(haveOld || haveNew);
if (!haveNew) ASSERT(newObj == nil);
Class previouslyInitializedClass = nil;
id oldObj;
SideTable *oldTable;
SideTable *newTable;
// Acquire locks for old and new values.
// Order by lock address to prevent lock ordering problems.
// Retry if the old value changes underneath us.
retry:
// 如果 weak指針之前弱引用過一個(gè) obj,則將這個(gè) obj 所對(duì)應(yīng)的 SideTable 取出闯狱,賦值給 oldTable
if (haveOld) {
oldObj = *location;// 將舊值(__weak指針的值) 賦給 oldObj
oldTable = &SideTables()[oldObj];// SideTablesMap.get();
} else {
oldTable = nil;
}
if (haveNew) {
// weak指針要弱引用一個(gè) obj煞赢,將這個(gè) obj 所對(duì)應(yīng)的 SideTable 取出,賦值給 onewTable
newTable = &SideTables()[newObj];
} else {
newTable = nil;
}
// lockTwo 加鎖哄孤,保證多線程安全
SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);
// 有無舊值 && location與oldObj 是否一致
// 如果 有舊值 但 location與oldObj 不同照筑,說明當(dāng)前的location已經(jīng)處理過oldObj,可是又被其他線程給修改了
if (haveOld && *location != oldObj) {
// 解鎖瘦陈, retry 重新處理 old
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
goto retry;
}
// 通過確保弱引用的對(duì)象沒有未初始化的isa凝危,防止 弱引用 和 初始化 之間死鎖
// Prevent a deadlock between the weak reference machinery
// and the +initialize machinery by ensuring that no
// weakly-referenced object has an un-+initialized isa.
if (haveNew && newObj) {
Class cls = newObj->getIsa();
if (cls != previouslyInitializedClass &&
!((objc_class *)cls)->isInitialized())
{ // cls != nil 且 cls 未初始化
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
class_initialize(cls, (id)newObj);// 去初始化 cls
// If this class is finished with +initialize then we're good.
// If this class is still running +initialize on this thread
// (i.e. +initialize called storeWeak on an instance of itself)
// then we may proceed but it will appear initializing and
// not yet initialized to the check above.
// Instead set previouslyInitializedClass to recognize it on retry.
// cls 賦值給 previouslyInitializedClass 重新 retry
previouslyInitializedClass = cls;
goto retry;
}
}
// Clean up old value, if any.
// 如果weak指針之前弱引用過別的對(duì)象oldObj,則調(diào)用weak_unregister_no_lock晨逝,在oldObj的weak_entry_t中移除該weak指針地址
if (haveOld) {
weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
}
// Assign new value, if any.
if (haveNew) {
newObj = (objc_object *)
// 調(diào)用weak_register_no_lock方法蛾默,將weak指針的地址記錄到newObj對(duì)應(yīng)的weak_entry_t中
// weak_entry_t 插入到 全局 weak_table 哈希表中
weak_register_no_lock(&newTable->weak_table, (id)newObj, location,
crashIfDeallocating);
// weak_register_no_lock returns nil if weak store should be rejected
// Set is-weakly-referenced bit in refcount table.
// 更新newObj的isa指針的weakly_referenced bit標(biāo)志位
if (newObj && !newObj->isTaggedPointer()) {
newObj->setWeaklyReferenced_nolock();
}
// Do not set *location anywhere else. That would introduce a race.
// weak指針直接指向了newObj,且沒有將newObj的引用計(jì)數(shù)+1
*location = (id)newObj;
}
else {
// No new value. The storage is not changed.
}
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
return (id)newObj;
}
第一次調(diào)用咏花,所以是一個(gè)新的對(duì)象趴生,也就是haveNew
的情況,獲取到的是新的散列表SideTable
昏翰,通過weak_register_no_lock()
方法來進(jìn)行插入苍匆。接著來分析weak_register_no_lock
函數(shù),是怎么注冊(cè)弱引用的.
1.3棚菊、注冊(cè)一個(gè)新對(duì)(對(duì)象浸踩,弱指針
) - weak_register_no_lock()
weak_register_no_lock()
-->
注冊(cè)一個(gè)新的(對(duì)象,弱指針)對(duì)
统求。如果弱對(duì)象entry
不存在检碗,則新建一個(gè)entry
:
/**
* Registers a new (object, weak pointer) pair. Creates a new weak
* object entry if it does not exist.
*
* @param weak_table The global weak table. // 全局的弱引用表
* @param referent The object pointed to by the weak reference. // 弱引用指向的對(duì)象
* @param referrer The weak pointer address. // weak指針地址
*/
id
weak_register_no_lock(weak_table_t *weak_table, id referent_id,
id *referrer_id, bool crashIfDeallocating)
{
objc_object *referent = (objc_object *)referent_id;
objc_object **referrer = (objc_object **)referrer_id;
//如果被弱引用的對(duì)象referent為nil 或者 被弱引用對(duì)象采用了 TaggedPointer 計(jì)數(shù)方式据块,直接返回
if (!referent || referent->isTaggedPointer()) return referent_id;
// ensure that the referenced object is viable
// 確認(rèn)引用對(duì)象是可用的 - 沒有被析構(gòu)且支持weak引用
bool deallocating;
if (!referent->ISA()->hasCustomRR()) {// isa 左移獲取是否析構(gòu)的值 has_cxx_dtor
deallocating = referent->rootIsDeallocating();
/**
inline bool
objc_object::rootIsDeallocating()
{
if (isTaggedPointer()) return false;
if (isa.nonpointer) return isa.deallocating;// isa的deallocating
return sidetable_isDeallocating();
}
*/
}
else {// has_cxx_dtor = YES
BOOL (*allowsWeakReference)(objc_object *, SEL) =
(BOOL(*)(objc_object *, SEL))
object_getMethodImplementation((id)referent,
@selector(allowsWeakReference));
if ((IMP)allowsWeakReference == _objc_msgForward) {
return nil;
}
deallocating =
! (*allowsWeakReference)(referent, @selector(allowsWeakReference));
}
// 正在析構(gòu),則不能被弱引用
if (deallocating) {
if (crashIfDeallocating) {// 拋異常
_objc_fatal("Cannot form weak reference to instance (%p) of "
"class %s. It is possible that this object was "
"over-released, or is in the process of deallocation.",
(void*)referent, object_getClassName((id)referent));
} else {// return nil
return nil;
}
}
// now remember it and where it is being stored
// 記錄存儲(chǔ)
// 在 weak_table 中找到被弱引用的對(duì)象referent所對(duì)應(yīng)的 weak_entry折剃,并將 referrer 加入到 weak_entry 中
weak_entry_t *entry;
if ((entry = weak_entry_for_referent(weak_table, referent))) {
// 已有 weak_entry 則將referent追加到weak_entry
append_referrer(entry, referrer);
}
else {
// 沒有另假,則新建 weak_entry,并將其插入weak_table
weak_entry_t new_entry(referent, referrer);
weak_grow_maybe(weak_table);// 存儲(chǔ)空間是否足夠怕犁,若已超出3/4边篮,則新開辟一個(gè)擴(kuò)容到2倍的空間,并把舊空間釋放free
weak_entry_insert(weak_table, &new_entry);// 插入new_entry
}
// Do not set *referrer. objc_storeWeak() requires that the
// value not change.
return referent_id;
}
1.3.1奏甫、插入新對(duì)象 weak_entry_insert()
:
/**
* Add new_entry to the object's table of weak references.
* Does not check whether the referent is already in the table.
*/
static void weak_entry_insert(weak_table_t *weak_table, weak_entry_t *new_entry)
{
weak_entry_t *weak_entries = weak_table->weak_entries;
ASSERT(weak_entries != nil);
// 插入位置 index
size_t begin = hash_pointer(new_entry->referent) & (weak_table->mask);// 哈希算法根據(jù) 弱引用對(duì)象 計(jì)算出開始插入的位置 index
size_t index = begin;
size_t hash_displacement = 0;
while (weak_entries[index].referent != nil) {
index = (index+1) & weak_table->mask;
if (index == begin) bad_weak_table(weak_entries);// 哈希沖突
hash_displacement++;
}
// while 直至 referent=nil戈轿,index 則是可插入位置
weak_entries[index] = *new_entry;
weak_table->num_entries++;
if (hash_displacement > weak_table->max_hash_displacement) {
// 更新 max_hash_displacement 的值
weak_table->max_hash_displacement = hash_displacement;
}
}
1.3.2、拼接 append_referrer()
:
/**
* Add the given referrer to set of weak pointers in this entry.
* Does not perform duplicate checking (b/c weak pointers are never
* added to a set twice).
*
* @param entry The entry holding the set of weak pointers.
* @param new_referrer The new weak pointer to be added.
*/
static void append_referrer(weak_entry_t *entry, objc_object **new_referrer)
{
// 判斷 weak_entry 是否可使用 靜態(tài)數(shù)組 inline_referrers
if (! entry->out_of_line()) {// weak_entry的out_of_line_ness == 2
// Try to insert inline. 嘗試將 new_referrer 插入數(shù)組
// WEAK_INLINE_COUNT = 4
// inline_referrers[4] : inline_referrers固定的4
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
if (entry->inline_referrers[i] == nil) {
entry->inline_referrers[i] = new_referrer;
return;
}
}
// 靜態(tài)數(shù)組位置滿了不能插入阵子,則開始使用 referrers動(dòng)態(tài)數(shù)組
// Couldn't insert inline. Allocate out of line.
// 開辟out of line空間思杯,空間大小: 4 * weak_referrer_t
weak_referrer_t *new_referrers = (weak_referrer_t *)
calloc(WEAK_INLINE_COUNT, sizeof(weak_referrer_t));
// This constructed table is invalid, but grow_refs_and_insert
// will fix it and rehash it.
// 這個(gè)構(gòu)造的表無效,但是grow_refs_and_insert將修復(fù)它并將他重新哈希挠进。
// for 循環(huán)色乾,將靜態(tài)組的數(shù)據(jù)賦到新建的動(dòng)態(tài)組中
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
new_referrers[i] = entry->inline_referrers[i];
}
entry->referrers = new_referrers;
entry->num_refs = WEAK_INLINE_COUNT;// num_refs 賦值 4
entry->out_of_line_ness = REFERRERS_OUT_OF_LINE;// 賦值 2
entry->mask = WEAK_INLINE_COUNT-1;// mask = 3
entry->max_hash_displacement = 0;// 最大哈希量初始化為0
}
ASSERT(entry->out_of_line());
// 如果動(dòng)態(tài)數(shù)組中元素個(gè)數(shù) ≥ 數(shù)組總空間的3/4,則將數(shù)組空間擴(kuò)容1倍奈梳,然后再將 new_referrer 插入數(shù)組
if (entry->num_refs >= TABLE_SIZE(entry) * 3/4) {
return grow_refs_and_insert(entry, new_referrer);
}
// 動(dòng)態(tài)數(shù)組空間足夠杈湾,開始計(jì)算 new_referrer 的 index,將 new_referrer 插入 weak_entry 中
// 計(jì)算 new_referrer 的插入位置 index
size_t begin = w_hash_pointer(new_referrer) & (entry->mask);
size_t index = begin;
size_t hash_displacement = 0;
while (entry->referrers[index] != nil) {
hash_displacement++;
index = (index+1) & entry->mask;
if (index == begin) bad_weak_table(entry);// 哈希沖突
}
if (hash_displacement > entry->max_hash_displacement) {
entry->max_hash_displacement = hash_displacement;// 最大哈希量 更新
}
// 將 new_referrer 插入 referrers 的 index 位置
weak_referrer_t &ref = entry->referrers[index];
ref = new_referrer;
entry->num_refs++;// 已存數(shù)量++
}
* 弱引用對(duì)象的存儲(chǔ)
- 總結(jié):
針對(duì)被弱引用的對(duì)象攘须,首先去全局弱引用表weak_table
的weak_entries[] 哈希數(shù)組
中尋找對(duì)應(yīng)的weak_entry_t
漆撞,
- 如果
weak_entry_t
不存在,則會(huì)新建一個(gè)于宙,并進(jìn)行空間是否夠用處理(已超出3/4則新開辟個(gè)2倍的空間并釋放舊空間
)浮驳,然后將新建的weak_entry_t
插入weak_table
的weak_entries數(shù)組
中對(duì)應(yīng)的index
(由哈希函數(shù)算出)位置捞魁; - 如果
weak_entry_t
存在至会,將指向 被弱引用的對(duì)象地址的指針 referrer 通過函數(shù)append_referrer()
,插入到對(duì)應(yīng)的weak_entry_t
中referrers
數(shù)組的index
位置谱俭,具體處理見上面源碼分析.
2奉件、weak
釋放 - 源碼分析
weak
銷毀的過程是調(diào)用析構(gòu)函數(shù)dealloc
,下面就來分析析構(gòu)函數(shù)的原理昆著。
- (void)dealloc {
_objc_rootDealloc(self);
}
void
_objc_rootDealloc(id obj)
{
ASSERT(obj);// obj 是否為空
obj->rootDealloc();
}
objc_object::rootDealloc()
inline void
objc_object::rootDealloc()
{
// 是 TaggedPointer 類型县貌,直接返回
if (isTaggedPointer()) return; // fixme necessary?
if (fastpath(isa.nonpointer && // nonpointer 開啟了優(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ù)
{
assert(!sidetable_present());
// 都沒有,直接 free
free(this);
}
else {
object_dispose((id)this);
}
}
不能直接釋放凑懂,繼續(xù) --> object_dispose()
:
/***********************************************************************
* object_dispose
* fixme
* Locking: none
**********************************************************************/
id
object_dispose(id obj)
{
if (!obj) return nil;
objc_destructInstance(obj);
free(obj);
return nil;
}
2.1煤痕、首先銷毀 C++析構(gòu)函數(shù)
和關(guān)聯(lián)對(duì)象
objc_destructInstance()
:
/***********************************************************************
* objc_destructInstance
* Destroys an instance without freeing memory.
* Calls C++ destructors.
* Calls ARC ivar cleanup.
* Removes associative references.
* Returns `obj`. Does nothing if `obj` is nil.
**********************************************************************/
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.
// 如果有C++的析構(gòu)函數(shù),則將C++析構(gòu)函數(shù)從類中銷毀
if (cxx) object_cxxDestruct(obj);
// 有關(guān)聯(lián)對(duì)象,將關(guān)聯(lián)對(duì)象全部移除摆碉,并將自己也從 assocationManager的map中移除
if (assoc) _object_remove_assocations(obj);
// 繼續(xù) dealloc 處理對(duì)象的其他引用
obj->clearDeallocating();
}
return obj;
}
2.2塘匣、銷毀其他
objc_object::clearDeallocating()
:
inline void
objc_object::clearDeallocating()
{
// 1. 要釋放的對(duì)象沒有采用優(yōu)化過的isa引用計(jì)數(shù)
if (slowpath(!isa.nonpointer)) {
// Slow path for raw pointer isa.
sidetable_clearDeallocating();
}
// 2. 要釋放的對(duì)象采用優(yōu)化過的isa引用計(jì)數(shù) 且 有弱引用 或 有用sidetable來引用計(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());
}
2.2.1、對(duì)象沒有采用優(yōu)化過的isa引用計(jì)數(shù)
sidetable_clearDeallocating()
:
void
objc_object::sidetable_clearDeallocating()
{
// 獲取當(dāng)前對(duì)象的 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();
// 在散列表中找到對(duì)應(yīng)的引用計(jì)數(shù)表 RefcountMap巷帝,拿到要釋放的對(duì)象的引用計(jì)數(shù)
RefcountMap::iterator it = table.refcnts.find(this);
if (it != table.refcnts.end()) {
if (it->second & SIDE_TABLE_WEAKLY_REFERENCED) {
// 從 weak_table 中忌卤,找到要釋放對(duì)象的 entry,對(duì)entry中referrers遍歷將數(shù)組中的弱引用 全部置為 nil
weak_clear_no_lock(&table.weak_table, (id)this);
}
// 從引用計(jì)數(shù)表中 擦除當(dāng)前對(duì)象的引用計(jì)數(shù)
table.refcnts.erase(it);
}
table.unlock();
}
2.2.2锅睛、對(duì)象采用了優(yōu)化過的isa引用計(jì)數(shù)
objc_object::clearDeallocating_slow()
:
// Slow path of clearDeallocating()
// for objects with nonpointer isa
// that were ever weakly referenced
// or whose retain count ever overflowed to the side table.
NEVER_INLINE void
objc_object::clearDeallocating_slow()
{
ASSERT(isa.nonpointer && (isa.weakly_referenced || isa.has_sidetable_rc));
// 獲取當(dāng)前對(duì)象散列表 sideTable
SideTable& table = SideTables()[this];
table.lock();
if (isa.weakly_referenced) {
// 有弱引用埠巨,將全部弱引用置為 nil
weak_clear_no_lock(&table.weak_table, (id)this);
}
if (isa.has_sidetable_rc) {
// 使用了sideTable的輔助引用計(jì)數(shù),直接在SideTable中擦除該對(duì)象的引用計(jì)數(shù)
table.refcnts.erase(this);
}
table.unlock();
}
弱引用了對(duì)象的 weak指針
置為 nil
weak_clear_no_lock(&table.weak_table, (id)this)
:
/**
* Called by dealloc; nils out all weak pointers that point to the
* provided object so that they can no longer be used.
*
* @param weak_table
* @param referent The object being deallocated.
*/
void
weak_clear_no_lock(weak_table_t *weak_table, id referent_id)
{
// 當(dāng)前被引用對(duì)象
objc_object *referent = (objc_object *)referent_id;
// 從 weak_table 中獲取 entry
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ù)組 referrers
// referrers 數(shù)組的 count
if (entry->out_of_line()) {
referrers = entry->referrers;
count = TABLE_SIZE(entry);
}
else {
referrers = entry->inline_referrers;
count = WEAK_INLINE_COUNT;
}
// 遍歷现拒,將referrers數(shù)組內(nèi)的 weak指針 全部置為 nil
for (size_t i = 0; i < count; ++i) {
objc_object **referrer = referrers[i];
if (referrer) {
// 如果weak指針 弱引用了對(duì)象 referent,則將weak指針設(shè)置為nil
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_table中移除對(duì)象的entry
weak_entry_remove(weak_table, entry);
}