總的來說:
weak_table 是 SideTable 的一個成員變量虱朵,避免直接操作 weak_table蛮穿。
根據(jù)當前對象指針,做一定偏移驻呐,找到對應的數(shù)組(SideTables)索引灌诅,再根據(jù)索引取>出這個 SideTable 。
你可以理解為key是對象指針含末,value就是 SideTable猜拾。
也就是說,一個對象答渔,對應一個 SideTable关带,一個 SideTable 對應一個 weak_table,
一個weak_table 存儲一個哈希表,key:當前對象宋雏,value:weak 引用
下面的代碼芜飘,都已經(jīng)去掉了無關緊要的,留下的都是重點部分:
首先看一下 SideTables , 實際上內(nèi)部維護了一個Map(key:指針 —— value:SideTable)
array :存儲的就是SideTable 數(shù)組
indexForPointer 方法磨总,這個方法根據(jù)實例的指針地址嗦明,返回它在 array 中的索引,從而>找到 SideTable
由此我們知道:每個實例蚪燕,SideTables 都會幫這個實例存儲一個SideTable
static objc::ExplicitInit<StripedMap<SideTable>> SideTablesMap;
static StripedMap<SideTable>& SideTables() {
return SideTablesMap.get();
}
StripedMap<T> is a map of [void* : T]
class StripedMap {
enum { StripeCount = 8 };
PaddedT array[StripeCount];
static unsigned int indexForPointer(const void *p) {
uintptr_t addr = reinterpret_cast<uintptr_t>(p);
return ((addr >> 4) ^ (addr >> 9)) % StripeCount;
}
public:
T& operator[] (const void *p) {
return array[indexForPointer(p)].value;
}
const T& operator[] (const void *p) const {
return const_cast<StripedMap<T>>(this)[p];
}
#if DEBUG
StripedMap() {
// Verify alignment expectations.
uintptr_t base = (uintptr_t)&array[0].value;
uintptr_t delta = (uintptr_t)&array[1].value - base;
ASSERT(delta % CacheLineSize == 0);
ASSERT(base % CacheLineSize == 0);
}
#else
constexpr StripedMap() {}
#endif
};
每個SideTable 都存儲了一個 weak_table 娶牌、引用計數(shù)的map、以及l(fā)ock
struct SideTable {
spinlock_t slock;
RefcountMap refcnts;
weak_table_t weak_table;
void lock() { slock.lock(); }
void unlock() { slock.unlock(); }
void forceReset() { slock.forceReset(); }
};
然后看weak_table:這是一個全局的weak 引用表馆纳。
weak_entries:存儲 weak_entry_t 列表
num_entries :weak 引用的個數(shù)
mask :用來做哈希表的mask
struct weak_table_t {
weak_entry_t *weak_entries;
size_t num_entries;
uintptr_t mask;
uintptr_t max_hash_displacement;
};
weak_entry_t: 存儲所有指向?qū)嵗膚eak引用
referent:當前實例對象指針:object point
inline_referrers: weak 變量指針(weak reference ) 集合诗良,引用低于4個的時候,用這個結(jié)構(gòu)存儲
referrers:weak 變量指針(weak reference ) 集合鲁驶,大于4個引用時候用這個存儲這里使用了 union 來共享內(nèi)存鉴裹,可以節(jié)約內(nèi)存的使用
當 out_of_line_ness == REFERRERS_OUT_OF_LINE 時,使用 referrers 來存儲weak >變量引用
當 out_of_line_ness != REFERRERS_OUT_OF_LINE 時钥弯,使用 inline_referrers 來存>儲weak 變量引用mask :一般用指針的地址 & mask 來找到對應的數(shù)組索引径荔,而mask 一般為數(shù)組的長度
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(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;
}
}
};
storeWeak 這個方法用來存儲新的指向當前實例的 weak 變量。
可以看到脆霎,先從 SideTables 拿到對應的 SideTable总处,
然后調(diào)用 weak_register_no_lock 把 newObj 存到了weak_table 里
static id
storeWeak(id *location, objc_object *newObj)
{
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:
newTable = &SideTables()[newObj];
newObj = (objc_object *) weak_register_no_lock(&newTable->weak_table, (id)newObj, location, crashIfDeallocating);
// Do not set *location anywhere else. That would introduce a race.
*location = (id)newObj;
return (id)newObj;
}
weak_entry_for_referent 這個方法就不寫了:實際就是找當前實例對象所屬的 weak_entry_t
這個方法是遍歷 weak_table->weak_entries ,然后檢查 weak_entries[index].referent >== referent
如果找到了睛蛛,就返回 weak_table->weak_entries[index]接下來我們看另外一個很重要的方法:append_referrer:
/**
* 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.
* @param referrer The weak pointer address.
*/
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;//weak 變量的指針的指針
// now remember it and where it is being stored
weak_entry_t *entry;
if ((entry = weak_entry_for_referent(weak_table, referent))) {
append_referrer(entry, referrer);
}
else {
weak_entry_t new_entry(referent, referrer);
weak_grow_maybe(weak_table);
weak_entry_insert(weak_table, &new_entry);
}
return referent_id;
}
把新的weak 變量引用鹦马,加到 entry里:
優(yōu)先使用 inline_referrers 存儲 weak 引用
如果inline_referrers 存儲滿了,則把 inline_referrers 拷貝到 referrers 里玖院,并且以后使用 referrers 來存儲菠红,并標記:out_of_line_ness = REFERRERS_OUT_OF_LINE; 這樣下次默認就使用 referrers 來存儲。如果存儲的引用數(shù)量超過了 3/4 难菌,則把 referrers 擴容 二倍试溯,再進行存儲。
存儲referrers 的階段郊酒,先把指針做一定偏移遇绞,然后 & mask,找到要存儲在數(shù)組里的位置: index燎窘,
如果這個index 已經(jīng)存儲了值摹闽,那么再次偏移、然后 & mask 褐健,遞歸來找付鹿,直到找到一個空的位置為止澜汤。
最后把 weak 引用存到這個數(shù)組的 index 位置里。當然舵匾,移除 weak 引用俊抵,和 append 引用是相似的道理,這里就不再贅述了坐梯。
static void append_referrer(weak_entry_t *entry, objc_object **new_referrer)
{
if (! entry->out_of_line()) {
// Try to insert inline.
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
if (entry->inline_referrers[i] == nil) {
entry->inline_referrers[i] = new_referrer;
return;
}
}
// Couldn't insert inline. Allocate out of line.
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.
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;
entry->out_of_line_ness = REFERRERS_OUT_OF_LINE;
entry->mask = WEAK_INLINE_COUNT-1;
entry->max_hash_displacement = 0;
}
if (entry->num_refs >= TABLE_SIZE(entry) * 3/4) {
return grow_refs_and_insert(entry, new_referrer);
}
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;
}
weak_referrer_t &ref = entry->referrers[index];
ref = new_referrer;
entry->num_refs++;
}