SideTables
SideTables 是全局表漆改,管理著對(duì)象的引用計(jì)數(shù)和weak引用指針互婿,每個(gè)對(duì)象在此表中都有對(duì)應(yīng)的一個(gè) SideTable崔涂,讓我們來(lái)看看 SideTables 源碼定義
static StripedMap<SideTable>& SideTables() {
return *reinterpret_cast<StripedMap<SideTable>*>(SideTableBuf);
}
雖然看不懂十兢,但從源碼的定義可以看出 SideTables 是通過(guò) StripedMap 來(lái)實(shí)現(xiàn)的勋颖,我們來(lái)看一下它的實(shí)現(xiàn)
template<typename T>
class StripedMap {
enum { CacheLineSize = 64 };
#if TARGET_OS_EMBEDDED // 嵌入式
enum { StripeCount = 8 };
#else
enum { StripeCount = 64 };
#endif
struct PaddedT {
T value alignas(CacheLineSize);
};
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];
}
};
從上可以看出侨赡,StripedMap 的容量大小為:StripeCount = 64蓖租,通過(guò) indexForPointer 函數(shù)分配在 StripedMap的下標(biāo)。public 中的應(yīng)該是讀存方法吧(猜測(cè)羊壹,哈哈蓖宦。。)油猫,可以看到其和 Map 集合一樣稠茂。
SideTable
SideTable 的定義如下
typedef objc::DenseMap<DisguisedPtr<objc_object>,size_t,true> RefcountMap;
struct SideTable {
spinlock_t slock;
RefcountMap refcnts;
weak_table_t weak_table;
...
};
- slock:是用于在對(duì) SideTable 操作時(shí),對(duì) SideTable 加鎖防止其他訪問(wèn)。
- refcnts:對(duì)象的引用計(jì)數(shù)器睬关,存儲(chǔ)著對(duì)象被引用的記錄诱担。
- weak_table_t: 存放弱變量引用
DisguisedPtr
class DisguisedPtr {
uintptr_t value;
// 對(duì)指針進(jìn)行偽裝
static uintptr_t disguise(T* ptr) {
return -(uintptr_t)ptr;
}
// 恢復(fù)至原指針
static T* undisguise(uintptr_t val) {
return (T*)-val;
}
};
DisguisedPtr 對(duì)指針進(jìn)行偽裝的類,將指針強(qiáng)轉(zhuǎn)為 uintptr_t (unsigned long)類型的負(fù)值电爹,這樣類似 leaks 這樣的查內(nèi)存泄漏的工具便無(wú)法跟蹤到對(duì)象蔫仙。
weak_table_t
struct weak_table_t {
weak_entry_t *weak_entries;
size_t num_entries;
uintptr_t mask;
uintptr_t max_hash_displacement;
};
- weak_entries:存放對(duì)象與弱引用對(duì)象指針映射的弱引用條目數(shù)組
- num_entries:弱引用條目總數(shù)
- mask:可存儲(chǔ)弱引用條目的容量
- max_hash_displacement:最大哈希偏移值
weak_entry_t
#define WEAK_INLINE_COUNT 4
#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 {
weak_referrer_t inline_referrers[WEAK_INLINE_COUNT];
};
};
};
weak_entry_t 是一個(gè)弱引用條目,其映射了引用對(duì)象和其被弱引用的指針丐箩,referent 便是引用對(duì)象摇邦,union(聯(lián)合體) 里存放著弱引用該對(duì)象的指針,union 里面的多個(gè)成員變量共享同一內(nèi)存空間屎勘。union 中有兩個(gè)結(jié)構(gòu)體都是存儲(chǔ)弱引用對(duì)象指針的集合施籍。第一個(gè)結(jié)構(gòu)體中 referrers 是一個(gè)可進(jìn)行擴(kuò)容的集合,而第二個(gè)結(jié)構(gòu)體中 inline_referrers 是一個(gè)容量為 4 的數(shù)組概漱,weak_entry_t 默認(rèn)使用 inline_referrers 來(lái)保存弱引用指針丑慎,當(dāng)此數(shù)組容量滿后,會(huì)使用 referrers 接管保存工作犀概。out_of_line_ness 便是描述存儲(chǔ)的弱引用指針是否超出 inline_referrers 的容量立哑。
__weak 原理
NSString *aa = @"aa";
__weak NSString *test = aa;
上面代碼在編譯時(shí),模擬的代碼如下:
NSString *aa;
aa = @"aa"
NSString *test;
objc_initWeak(&obj, aa);
objc_destoryWeak(&obj);
__weak 變量創(chuàng)建
__weak 變量的創(chuàng)建入口是 objc_initWeak 這個(gè)函數(shù)姻灶,其實(shí)現(xiàn)是:
id objc_initWeak(id *location, id newObj)
{
if (!newObj) {
*location = nil;
return nil;
}
return storeWeak<false/*old*/, true/*new*/, true/*crash*/>
(location, (objc_object*)newObj);
}
如果 __weak 變量被賦予的對(duì)象是 nil 那么,將 __weak 變量置 nil诈茧,進(jìn)入 objc_destoryWeak 銷毀函數(shù)产喉。storeWeak 函數(shù)是一個(gè)更新弱變量的函數(shù),此函數(shù)有點(diǎn)長(zhǎng)敢会,我們分段講述:
// 如果 HaveOld 為 true曾沈,則表明變量需要清理,變量可能為nil
// 如果 HaveNew 為 true鸥昏,則表明有一個(gè)新值將賦予變量塞俱,這個(gè)新值可能為 nil
// 如果 CrashIfDeallocating 為 true,則表明新值 newObj 是釋放了的對(duì)象(并不是說(shuō) newObj 為 nil)或者是一個(gè)不支持弱引用的對(duì)象吏垮。
// 如果 CrashIfDeallocating 為 false障涯,則將新值 newObj 置nil 并 *location 弱變量賦值為 nil。
template <bool HaveOld, bool HaveNew, bool CrashIfDeallocating>
static id
storeWeak(id *location, objc_object *newObj)
{
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.
// 按鎖地址排序以防止鎖排序問(wèn)題
// Retry if the old value changes underneath us.
// 如果下面的舊值發(fā)生更改唯蝶,請(qǐng)重試。
retry:
if (HaveOld) {
oldObj = *location;
oldTable = &SideTables()[oldObj];
} else {
oldTable = nil;
}
if (HaveNew) {
newTable = &SideTables()[newObj];
} else {
newTable = nil;
}
聲明一個(gè) previouslyInitializedClass 保存先前初始化的類遗嗽,聲明一個(gè)舊值對(duì)象 oldObj粘我,一新一舊兩個(gè) SideTable(散列表)。從 objc_initWeak 傳入的 HaveOld 為 false痹换,HaveNew 為 true征字,因此將 oldTable 賦值為 nil都弹,從 SideTables 獲取 newObj 的 SideTable 賦值給 newTable。兩個(gè)散列表處理好了后匙姜,因?yàn)楫?dāng)前是 __weak 變量的創(chuàng)建缔杉,處理的是新值,所以下面只給出新值有關(guān)的處理代碼
// 給新舊散列表加鎖
SideTable::lockTwo<HaveOld, HaveNew>(oldTable, newTable);
// 通過(guò)確保沒(méi)有弱引用對(duì)象具有未初始化的isa搁料,防止弱引用機(jī)制和初始化機(jī)制之間的死鎖或详。
if (HaveNew && newObj) {
Class cls = newObj->getIsa();
if (cls != previouslyInitializedClass &&
!((objc_class *)cls)->isInitialized())
{
SideTable::unlockTwo<HaveOld, HaveNew>(oldTable, newTable);
_class_initialize(_class_getNonMetaClass(cls, (id)newObj));
// 如果這個(gè)類完成了+initialize,那最好郭计。如果這個(gè)類仍在這個(gè)線程上運(yùn)行+initialize(即+initialize霸琴,調(diào)用storeWeak,在其自身的實(shí)例上)昭伸,我們可以繼續(xù)梧乘,但是將顯示為初始化和尚未初始化作為上述檢查,設(shè)置 previouslyInitializedClass 以在重試時(shí)識(shí)別它
previouslyInitializedClass = cls;
goto retry;
}
}
此步驟為確保弱引用對(duì)象 newObj 初始化了庐杨,首先通過(guò)獲取 newObj 的 isa 指針獲取它的類选调,然后判斷它的類是否初始化了,如果沒(méi)有灵份,便打開(kāi)新舊散列表的鎖仁堪,獲取 newObj 的元類發(fā)送 +initialize 消息進(jìn)行初始化。下面是 storeWeak 函數(shù)最后一部分:
// Assign new value, if any.
if (HaveNew) {
// 弱引用注冊(cè)失敗便返回 nil
newObj = (objc_object *)weak_register_no_lock(&newTable->weak_table, (id)newObj, location, CrashIfDeallocating);
// 設(shè)置refcount表中的弱引用位
if (newObj && !newObj->isTaggedPointer()) {
newObj->setWeaklyReferenced_nolock();
}
// 不要在其他地方設(shè)置 *location填渠,否則會(huì)有沖突弦聂。
*location = (id)newObj;
}
// 打開(kāi)新舊散列表的鎖
SideTable::unlockTwo<HaveOld, HaveNew>(oldTable, newTable);
// 弱引用處理完畢,返回新值
return (id)newObj;
首先氛什,通過(guò) weak_register_no_lock 函數(shù)將 __weak 變量對(duì) newObj 的弱引用注冊(cè)到 newObj的散列表的弱引用表中莺葫,如果注冊(cè)成功則設(shè)置 newObj 的refcount表中的 __weak 變量對(duì)其的引用為弱引用,然后將新值賦給 __weak 變量枪眉。最后捺檬,所有操作完成了,打開(kāi)新舊散列表的鎖贸铜,返回新值賦給 __weak 變量堡纬。
弱引用注冊(cè)
__weak 變量引用對(duì)象時(shí),需要將 __weak 變量的弱引用注冊(cè)到被引用對(duì)象的弱引用表中萨脑,這一操作便由 weak_register_no_lock 函數(shù)完成隐轩。此函數(shù)的實(shí)現(xiàn)我們分兩部分程呈現(xiàn):
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;
if (!referent || referent->isTaggedPointer()) return referent_id;
// ensure that the referenced object is viable
// 確保引用的對(duì)象是可用的
bool deallocating;
if (!referent->ISA()->hasCustomRR()) {
deallocating = referent->rootIsDeallocating();
}
else {
BOOL (*allowsWeakReference)(objc_object *, SEL) =
(BOOL(*)(objc_object *, SEL))
object_getMethodImplementation((id)referent,
SEL_allowsWeakReference);
if ((IMP)allowsWeakReference == _objc_msgForward) {
return nil;
}
deallocating =
! (*allowsWeakReference)(referent, SEL_allowsWeakReference);
}
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;
}
}
上部分是為了確保弱引用的對(duì)象 referent(newObj對(duì)象)支持被弱引用。首先判斷引用對(duì)象 referent 的 isa 中是否有自定義 retain 和 release 實(shí)現(xiàn)渤早,如果沒(méi)有职车,則調(diào)用 rootIsDeallocating() 函數(shù)檢查 referent 是否在析構(gòu)(即是否被釋放)。
如果referent 的 isa 中有自定義 retain 和 release 實(shí)現(xiàn),首先會(huì)獲取 referent 中 SEL_allowsWeakReference 方法的實(shí)現(xiàn)悴灵,如果獲取的是 _objc_msgForward 消息轉(zhuǎn)發(fā)函數(shù)扛芽,那么表明該引用對(duì)象不支持弱引用。反之积瞒,便發(fā)送 SEL_allowsWeakReference 消息去判斷該對(duì)象是否支持弱引用川尖,如果支持則表示 referent 引用對(duì)象不在析構(gòu)。
如果該引用對(duì)象在析構(gòu)并且 crashIfDeallocating(控制引用對(duì)象析構(gòu)是否需crash)為true茫孔,則crash叮喳。如果 crashIfDeallocating 為 false,則返回 nil 表示注冊(cè)弱引用失敗缰贝。
// 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);
}
// Do not set *referrer. objc_storeWeak() requires that the
// value not change.
return referent_id;
}
這下半部分就是將 __weak 變量的弱引用指針存儲(chǔ)到被引用對(duì)象 newObj 的弱引用表中馍悟,完成注冊(cè)。首先通過(guò) weak_entry_for_referent 函數(shù)去查找 newObj 對(duì)應(yīng)的 SideTable 的 weak_table 表中的對(duì)應(yīng) newObj 的弱引用條目 entry剩晴。如果不存在 entry锣咒,則用 newobj 和 __weak變量指針生成一個(gè)新的弱引用條目 new_entry。接下來(lái)執(zhí)行 weak_grow_maybe 函數(shù)看 weak_table 是否需要擴(kuò)容赞弥。
weak_grow_maybe 和 weak_resize
#define TABLE_SIZE(entry) (entry->mask ? entry->mask + 1 : 0)
static void weak_grow_maybe(weak_table_t *weak_table)
{
size_t old_size = TABLE_SIZE(weak_table);
// Grow if at least 3/4 full.
// 如果當(dāng)前條目數(shù)已滿容量的 3/4 則允許擴(kuò)容
// 如果是初次的話擴(kuò)容 64毅整,之后以 2 倍增加
if (weak_table->num_entries >= old_size * 3 / 4) {
weak_resize(weak_table, old_size ? old_size*2 : 64);
}
}
如果 newObj 是第一次被引用,那么其對(duì)應(yīng)的 weak_table 的容量 mask 應(yīng)為 0绽左,則 old_size = 0悼嫉, weak_table 的弱引用條目總數(shù)自然也為 0。滿足擴(kuò)容條件妇菱,因此初次擴(kuò)容為 64承粤,執(zhí)行 weak_resize(weak_table, 64)。
weak_resize 是對(duì) weak_table 擴(kuò)容的函數(shù)闯团,其實(shí)現(xiàn)如下:
static void weak_resize(weak_table_t *weak_table, size_t new_size)
{
size_t old_size = TABLE_SIZE(weak_table);
weak_entry_t *old_entries = weak_table->weak_entries;
weak_entry_t *new_entries = (weak_entry_t *)
calloc(new_size, sizeof(weak_entry_t));
weak_table->mask = new_size - 1;
weak_table->weak_entries = new_entries;
weak_table->max_hash_displacement = 0;
weak_table->num_entries = 0; // restored by weak_entry_insert below
if (old_entries) {
weak_entry_t *entry;
weak_entry_t *end = old_entries + old_size;
for (entry = old_entries; entry < end; entry++) {
if (entry->referent) {
weak_entry_insert(weak_table, entry);
}
}
free(old_entries);
}
}
創(chuàng)建一個(gè) weak_entry_t 實(shí)例 old_entries 保存 weak_table 弱引用表中的弱引用條目列表,然后創(chuàng)建一個(gè) 64 格的新弱引用條目列表仙粱,接著更新設(shè)置 weak_table 的容量大小房交、弱引用條目列表、最大哈希位移數(shù)伐割、條目總數(shù)候味。最后,如果舊弱引用條目列表 old_entries 存在數(shù)據(jù)隔心,則將舊條目列表的數(shù)據(jù)插入 weak_table 新擴(kuò)容的條目列表中并釋放舊條目列表白群。
擴(kuò)容完后,便開(kāi)始將新創(chuàng)建的條目插入 weak_table 的條目列表 weak_entries 中硬霍。
weak_entry_insert
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);
size_t begin = hash_pointer(new_entry->referent) & (weak_table->mask);
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++;
}
weak_entries[index] = *new_entry;
weak_table->num_entries++;
if (hash_displacement > weak_table->max_hash_displacement) {
weak_table->max_hash_displacement = hash_displacement;
}
}
通過(guò) hash_pointer(new_entry->referent) 和 weak_table->mask 的與運(yùn)算決定新弱引用條目在 weak_entries 的初始下標(biāo)帜慢,如果 weak_entries 中該下標(biāo)中沒(méi)被用,則將 new_entry 存放在此處,weak_table 的 num_entries 自增長(zhǎng) 1粱玲。
如果該初始下標(biāo)中已被存放了條目躬柬,則循環(huán)將 hash_pointer() 計(jì)算的 hash值 + 1 再次與 weak_table->mask 進(jìn)行“與運(yùn)算”并且哈希位移數(shù)自增加 1,如果沒(méi)找到可存儲(chǔ)的位置則會(huì)執(zhí)行 bad_weak_table 報(bào) “This may be a runtime bug or a memory error somewhere else.” 錯(cuò)誤抽减。
如果找到可用的位置允青,則 new_entry 將存放到 weak_entries 中。最后卵沉,如果哈希位移數(shù)大于 weak_table 中存儲(chǔ)的最大哈希位移數(shù)颠锉,則更新 weak_table 中的 max_hash_displacement 值為 hash_displacement。 到此處史汗,弱引用的注冊(cè)也就完成了琼掠。
append_referrer
如果 newObj 是一個(gè)被其他變量弱引用的對(duì)象,那么能通過(guò) weak_entry_for_referent 函數(shù)找到 newObj 對(duì)應(yīng)的弱引用條目淹办。將 __weak 變量的指針保存到弱引用條目的引用指針數(shù)組中完成注冊(cè)眉枕,我們來(lái)看看這個(gè)過(guò)程是怎么樣的
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;
}
上部分,判斷弱引用條目中存放的引用指針數(shù)超過(guò)了 inline_referrers 數(shù)組的容量怜森。如果沒(méi)有超過(guò)的話(有可能容量已滿)速挑,則遍歷 inline_referrers 找到空位置存放 new_referrer。如果 inline_referrers 容量已滿副硅,改用 entry 的 referrers 列表存放引用指針姥宝。首先,將 inline_referrers 中存放的引用指針加到 referrers 中恐疲,更新設(shè)置 num_refs腊满、out_of_line_ness(是否超出了inline_referrers數(shù)組的容量)、mask培己、max_hash_displacement碳蛋。接下來(lái)就進(jìn)入下部分
assert(entry->out_of_line());
if (entry->num_refs >= TABLE_SIZE(entry) * 3/4) {
return grow_refs_and_insert(entry, new_referrer);
}
如果 entry 的引用指針數(shù)達(dá)到了存放容量的 3/4,那么對(duì) new_referrer 進(jìn)行擴(kuò)容并且插入 new_referrer肃弟。
grow_refs_and_insert
#define TABLE_SIZE(entry) (entry->mask ? entry->mask + 1 : 0)
static void grow_refs_and_insert(weak_entry_t *entry,
objc_object **new_referrer)
{
assert(entry->out_of_line());
size_t old_size = TABLE_SIZE(entry);
size_t new_size = old_size ? old_size * 2 : 8;
size_t num_refs = entry->num_refs;
weak_referrer_t *old_refs = entry->referrers;
entry->mask = new_size - 1;
entry->referrers = (weak_referrer_t *)
calloc(TABLE_SIZE(entry), sizeof(weak_referrer_t));
entry->num_refs = 0;
entry->max_hash_displacement = 0;
for (size_t i = 0; i < old_size && num_refs > 0; i++) {
if (old_refs[i] != nil) {
append_referrer(entry, old_refs[i]);
num_refs--;
}
}
// Insert
append_referrer(entry, new_referrer);
if (old_refs) free(old_refs);
}
這個(gè)函數(shù)和 weakTable 的擴(kuò)容函數(shù) weak_resize 一樣箩兽,首先通過(guò) old_size 計(jì)算出擴(kuò)容的大小 new_size,然后創(chuàng)建一個(gè) weak_referrer_t 實(shí)例 old_refs 存放 entry 中 referrers 列表的引用指針,然后對(duì) entry 的 referrers 進(jìn)行初始化擴(kuò)容。最后,如果 old_refs 有數(shù)據(jù)(即原 entry 存在的引用指針)许师,將引用指針通過(guò) append_referrer 插入到擴(kuò)容后的 referrers 中,此步驟為遞歸調(diào)用。插入的主要代碼便是 append_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++;
}
上方代碼和 weakTable 的 weak_entry_insert 函數(shù)實(shí)現(xiàn)原理一樣,便不再講述。從上面分析下來(lái)的猛,可見(jiàn) weakTable 的weak_entries 和 entry 的referrers 一樣是一個(gè)可以自動(dòng)擴(kuò)容的數(shù)組,而 entry 的inline_referrers 是一個(gè)不可擴(kuò)容的數(shù)組。
__weak 變量銷毀
__weak 變量銷毀會(huì)調(diào)用 objc_destroyWeak 這個(gè)函數(shù)
void
objc_destroyWeak(id *location)
{
(void)storeWeak<true/*old*/, false/*new*/, false/*crash*/>
(location, nil);
}
其中實(shí)現(xiàn)也是通過(guò) storeWeak 函數(shù)將 __weak 變量置為nil正驻,下面只顯示相關(guān)代碼
template <bool HaveOld, bool HaveNew, bool CrashIfDeallocating>
static id
storeWeak(id *location, objc_object *newObj)
{
assert(HaveOld || HaveNew);
if (!HaveNew) assert(newObj == nil);
Class previouslyInitializedClass = nil;
id oldObj;
SideTable *oldTable;
SideTable *newTable;
retry:
if (HaveOld) {
oldObj = *location;
oldTable = &SideTables()[oldObj];
} else {
oldTable = nil;
}
if (HaveNew) {
newTable = &SideTables()[newObj];
} else {
newTable = nil;
}
SideTable::lockTwo<HaveOld, HaveNew>(oldTable, newTable);
if (HaveOld && *location != oldObj) {
SideTable::unlockTwo<HaveOld, HaveNew>(oldTable, newTable);
goto retry;
}
// Clean up old value, if any.
if (HaveOld) {
weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
}
SideTable::unlockTwo<HaveOld, HaveNew>(oldTable, newTable);
return (id)newObj;
}
從 objc_destroyWeak 函數(shù)傳入的 HaveOld = true氓栈、HaveNew = false渣磷、CrashIfDeallocating = false,首先將 __weak 變量的內(nèi)存指針指向 oldObj授瘦,將當(dāng)前值變成舊值醋界,對(duì)應(yīng)的從 SideTables 取出對(duì)應(yīng)的 SideTable 為 oldTable,將 newTable 賦值為 nil提完。然后形纺,將 oldTable 和 newTable 加鎖,如果舊值存在并且舊值 __weak 變量?jī)?nèi)存地址中的值和舊值不相等的話徒欣,那么需要重新執(zhí)行第一步驟以保證銷毀工作進(jìn)行逐样。接下來(lái)就是注銷 oldObj 對(duì)應(yīng)的 weak_table 中 __weak 變量的弱引用。最后打肝,解開(kāi) oldTable 和 newTable 的鎖脂新,返回 nil,將 __weak 變量置 nil粗梭。
weak_unregister_no_lock
void
weak_unregister_no_lock(weak_table_t *weak_table, id referent_id,
id *referrer_id)
{
objc_object *referent = (objc_object *)referent_id;
objc_object **referrer = (objc_object **)referrer_id;
weak_entry_t *entry;
if (!referent) return;
if ((entry = weak_entry_for_referent(weak_table, referent))) {
remove_referrer(entry, referrer);
bool empty = true;
if (entry->out_of_line() && entry->num_refs != 0) {
empty = false;
}
else {
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
if (entry->inline_referrers[i]) {
empty = false;
break;
}
}
}
if (empty) {
weak_entry_remove(weak_table, entry);
}
}
// Do not set *referrer = nil. objc_storeWeak() requires that the
// value not change.
}
首先争便,通過(guò) weak_entry_for_referent 找到 weak_table 中的弱引用條目 entry,然后通過(guò) remove_referrer 函數(shù)從 entry 的引用指針列表中刪除 __weak變量指針断医。如果 entry 中沒(méi)有引用指針了滞乙,那么便會(huì)執(zhí)行 weak_entry_remove 從弱引用表 weak_table 中刪除該弱引用條目奏纪。
remove_referrer
static void remove_referrer(weak_entry_t *entry, objc_object **old_referrer)
{
if (! entry->out_of_line()) {
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
if (entry->inline_referrers[i] == old_referrer) {
entry->inline_referrers[i] = nil;
return;
}
}
_objc_inform("Attempted to unregister unknown __weak variable "
"at %p. This is probably incorrect use of "
"objc_storeWeak() and objc_loadWeak(). "
"Break on objc_weak_error to debug.\n",
old_referrer);
objc_weak_error();
return;
}
size_t begin = w_hash_pointer(old_referrer) & (entry->mask);
size_t index = begin;
size_t hash_displacement = 0;
while (entry->referrers[index] != old_referrer) {
index = (index+1) & entry->mask;
if (index == begin) bad_weak_table(entry);
hash_displacement++;
if (hash_displacement > entry->max_hash_displacement) {
_objc_inform("Attempted to unregister unknown __weak variable "
"at %p. This is probably incorrect use of "
"objc_storeWeak() and objc_loadWeak(). "
"Break on objc_weak_error to debug.\n",
old_referrer);
objc_weak_error();
return;
}
}
entry->referrers[index] = nil;
entry->num_refs--;
}
如果,entry 的引用指針數(shù)不超過(guò) inline_referrers 的容量斩启,那么遍歷 inline_referrers 找到引用指針的位置并置為nil序调。如果引用指針數(shù)不超過(guò) inline_referrers 的容量,那么便得去 referrers 中找到引用指針置為nil并將 referrers 的長(zhǎng)度減一兔簇。如果找不到便會(huì)調(diào)用 objc_weak_error()
weak_entry_remove
static void weak_entry_remove(weak_table_t *weak_table, weak_entry_t *entry)
{
// remove entry
if (entry->out_of_line()) free(entry->referrers);
bzero(entry, sizeof(*entry));
weak_table->num_entries--;
weak_compact_maybe(weak_table);
}
此函數(shù)將弱引用條目 entry 從 weak_table 中刪除发绢,然后通過(guò) weak_compact_maybe 去檢查是否需要縮小 weak_table 的容量。
weak_compact_maybe
#define TABLE_SIZE(entry) (entry->mask ? entry->mask + 1 : 0)
static void weak_compact_maybe(weak_table_t *weak_table)
{
size_t old_size = TABLE_SIZE(weak_table);
// Shrink if larger than 1024 buckets and at most 1/16 full.
if (old_size >= 1024 && old_size / 16 >= weak_table->num_entries) {
weak_resize(weak_table, old_size / 8);
// leaves new table no more than 1/2 full
}
}
如果達(dá)到縮小容量大小的要求男韧,便通過(guò) weak_resize 函數(shù)調(diào)整容量為原來(lái)的 1/8朴摊。