1. weak關(guān)鍵字
用weak
關(guān)鍵字修飾的對象指針是弱引用,被引用對象的引用計數(shù)不會+1,并在引用對象被釋放
的時候自動被設(shè)置為nil
。通常用于解決循環(huán)引用
問題泞遗,如代理以及及block
的使用中,相對會較多的使用到weak
席覆。
為了能夠更深入的理解 weak
的底層實現(xiàn)史辙,應(yīng)對面試問到關(guān)于weak
實現(xiàn)的問題,這次記錄并學(xué)習(xí)一下 weak
實現(xiàn)的源碼佩伤。
2. 實現(xiàn)的大概流程
我們從下面這段代碼開始聊倔,來學(xué)習(xí)一下weak
實現(xiàn)的源碼:
{
NSObject *obj = [[NSObject alloc] init];
id __weak obj1 = obj;
}
創(chuàng)建weak
引用的時候,會走到runtime
的 objc_initWeak
的這個方法里生巡,可以通過符號斷點進行驗證耙蔑,源碼如下:
// location 是指向?qū)ο蟮娜踔羔樀牡刂?// newObj 是對象指針
id objc_initWeak(id *location, id newObj)
{
if (!newObj) {
*location = nil;
return nil;
}
return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
(location, (objc_object*)newObj);
}
從上面可以看到會走到storeWeak
方法中,源碼如下:
/*
*location : weak指針的地址
newObj : 被指向的對象
*/
template <HaveOld haveOld, HaveNew haveNew,
CrashIfDeallocating 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) {
// 如果weak 指針之前指向過其他對象孤荣,取出這個舊對象
oldObj = *location;
// 以舊對象為 key甸陌,從全局 SideTables()中取出舊對象對應(yīng)的 SideTable
oldTable = &SideTables()[oldObj];
} else {
oldTable = nil;
}
if (haveNew) {
// 如果有新值须揣,那么就取出新值對應(yīng)的 SideTable
newTable = &SideTables()[newObj];
} else {
newTable = nil;
}
SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);
if (haveOld && *location != oldObj) {
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
goto retry;
}
// 保證弱引用的對象的 isa 非空,防止弱引用機制和+initialize發(fā)生死鎖
if (haveNew && newObj) {
Class cls = newObj->getIsa();
if (cls != previouslyInitializedClass &&
!((objc_class *)cls)->isInitialized())
{
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
class_initialize(cls, (id)newObj);
previouslyInitializedClass = cls;
goto retry;
}
}
// 如果之前weak 指針指向了別的對象钱豁,這里清空
if (haveOld) {
weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
}
if (haveNew) {
// 通過 newObj 和 location 生成一個新的 weak_entry_t并插入到 newObj 的弱引用數(shù)組中
newObj = (objc_object *)
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
if (newObj && !newObj->isTaggedPointer()) {
// 設(shè)置被引用對象 isa 的弱引用標(biāo)志位為 YES
newObj->setWeaklyReferenced_nolock();
}
// Do not set *location anywhere else. That would introduce a race.
*location = (id)newObj;
}
else {
// No new value. The storage is not changed.
}
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
return (id)newObj;
}
從上面這個方法中我們可以大概了解了圍繞 weak
指針進行的一些操作:
- 從全局
SideTables()
中獲取對象所在的SideTable
-
isa
的非空校驗耻卡,如果isa
沒有被初始化,則執(zhí)行class_initialize(cls, (id)newObj)
方法 - 如果
weak 指針
之前指向了別的對象牲尺,就解除對舊對象的引用
- 注冊新對象的弱引用
- 設(shè)置新對象的
弱引用標(biāo)志符為 YES
大概的流程圖如下:
3. 散列表的相關(guān)結(jié)構(gòu)
上面storeWeak
方法中卵酪,我們看到有一個全局的 SideTables()
散列表,我們以此為切入點谤碳,去查看一下關(guān)于weak
實現(xiàn)涉及到的整體結(jié)構(gòu)溃卡。
3.1 SideTables()
首先,SideTables()
是一個靜態(tài)函數(shù)估蹄,代碼如下:
static objc::ExplicitInit<StripedMap<SideTable>> SideTablesMap;
static StripedMap<SideTable>& SideTables() {
return SideTablesMap.get();
}
函數(shù)體里面調(diào)用了一個全局的靜態(tài)變量SideTablesMap
的 get()
方法塑煎,這個靜態(tài)變量保存了所有的SideTable
沫换,是objc
命名空間下的一個ExplicitInit
類臭蚁,它里面實現(xiàn)了get()
方法,如下:
Type &get() {
return *reinterpret_cast<Type *>(_storage);
}
這個get()
方法其實返回的就是StripedMap
類的實例讯赏。
3.2 StripedMap
StripedMap 是一個以void *p
為 key垮兑,PaddedT
為 value 的表,實現(xiàn)如下:
template<typename T>
class StripedMap {
#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
enum { StripeCount = 8 }; // 真機下StripedMap存儲的 SideTable 數(shù)量為 8
#else
enum { StripeCount = 64 }; // 模擬器下為 64
#endif
// 對于SideTable的封裝
struct PaddedT {
T value alignas(CacheLineSize);
};
// 存儲 PaddedT 的散列表
PaddedT array[StripeCount];
// 散列函數(shù)漱挎,通過對象地址計算出對應(yīng) PaddedT在數(shù)組中的下標(biāo)
static unsigned int indexForPointer(const void *p) {
uintptr_t addr = reinterpret_cast<uintptr_t>(p);
return ((addr >> 4) ^ (addr >> 9)) % StripeCount;
}
public:
// 取值操作系枪,重寫了[ ]方法,上面提到的&SideTables()[oldObj]會調(diào)用到這個方法
T& operator[] (const void *p) {
return array[indexForPointer(p)].value;
}
const T& operator[] (const void *p) const {
return const_cast<StripedMap<T>>(this)[p];
}
// ... 省略一些方法
};
綜上可以得出 SideTables
的結(jié)構(gòu)實際上如下圖所示:
3.3 SideTable
上面介紹StripedMap
時磕谅,其內(nèi)部有一個哈希表私爷,表中存儲的是 PaddedT
結(jié)構(gòu)體,結(jié)構(gòu)體的 value
就是 SideTable
膊夹,其實現(xiàn)如下:
struct SideTable {
spinlock_t slock; // 保證原子操作的自旋鎖
RefcountMap refcnts; // 引用計數(shù)的 hash 表
weak_table_t weak_table; // weak 引用全局 hash 表
// 構(gòu)造函數(shù)
SideTable() {
memset(&weak_table, 0, sizeof(weak_table));
}
// 析構(gòu)函數(shù)
~SideTable() {
_objc_fatal("Do not delete SideTable.");
}
};
我們現(xiàn)在把重點放在weak_table_t weak_table
上衬浑。
SideTable
結(jié)構(gòu)圖如下:
3.4 weak_table_t
上面我們可以看到SideTable
結(jié)構(gòu)體中有一個weak_table_t
結(jié)構(gòu)體類型的成員變量,實現(xiàn)如下:
// 全局弱引用表
struct weak_table_t {
weak_entry_t *weak_entries; // hash 數(shù)組放刨,用來存儲弱引用對象相關(guān)信息的 weak_entry_t
size_t num_entries; // hash數(shù)組中元素的個數(shù)
uintptr_t mask; // hash 數(shù)組的長度(并不是實際的存儲個數(shù))-1工秩,主要用來參與哈希函數(shù)
uintptr_t max_hash_displacement; // 最大哈希偏移值
};
結(jié)構(gòu)圖如下:
3.5 weak_entry_t
上面的weak_table_t
中可以看到其內(nèi)部有一個weak_entry_t
的結(jié)構(gòu)體數(shù)組,這就是其內(nèi)部維護的哈希表进统,我們現(xiàn)在來看下weak_entry_t
結(jié)構(gòu)體的實現(xiàn)助币,如下:
struct weak_entry_t {
DisguisedPtr<objc_object> referent; // 弱引用的對象
union {
// 弱引用數(shù)量大于 4 個用到的結(jié)構(gòu)體
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;
};
// 弱引用數(shù)量不大于 4 個用到的結(jié)構(gòu)體
struct {
// out_of_line_ness field is low bits of inline_referrers[1]
weak_referrer_t inline_referrers[WEAK_INLINE_COUNT]; // 存儲指向該對象的弱引用數(shù)組
};
};
// 判斷是否用的是 referrers 來存儲弱引用指針
bool out_of_line() {
return (out_of_line_ness == REFERRERS_OUT_OF_LINE);
}
// 覆蓋老數(shù)據(jù)
weak_entry_t& operator=(const weak_entry_t& other) {
memcpy(this, &other, sizeof(other));
return *this;
}
// 構(gòu)造方法
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;
}
}
};
從上面的代碼實現(xiàn)中可以看出,weak_entry_t
結(jié)構(gòu)體存放的是某個對象的所有弱引用指針螟碎,存放所有弱引用指針使用的是一個聯(lián)合體眉菱,如果弱引用指針的數(shù)量不超過 4 個就用inline_referrers
存儲,否則用referrers
存儲掉分。
結(jié)構(gòu)圖如下:
3.6 weak_referrer_t
weak_entry_t
用于存放所有指向某個對象的 weak
指針地址俭缓,類型是weak_entry_t
迈螟,實現(xiàn)如下:
typedef DisguisedPtr<objc_object *> weak_referrer_t;
3.7 結(jié)構(gòu)圖總結(jié)
4. 代碼結(jié)合結(jié)構(gòu)圖具體分析
我們還從??上面提到的storeWeak
方法中進行具體分析,代碼如下尔崔,標(biāo)注的一些點在下面會進行具體分析:
template <HaveOld haveOld, HaveNew haveNew,
CrashIfDeallocating crashIfDeallocating>
storeWeak(id *location, objc_object *newObj)
{
//校驗舊對象和新對象必須存其一
ASSERT(haveOld || haveNew);
//校驗如果haveNew=true答毫,newObj不能為nil
if (!haveNew) ASSERT(newObj == nil);
Class previouslyInitializedClass = nil;
id oldObj;
SideTable *oldTable;
SideTable *newTable;
retry:
if (haveOld) {
//如果weak 指針指向舊值,就取出舊值
oldObj = *location;
//以舊對象地址為 key取出舊的SideTable
oldTable = &SideTables()[oldObj];
} else {
oldTable = nil;
}
if (haveNew) {
// 取出對應(yīng)新的SideTable
newTable = &SideTables()[newObj];
} else {
newTable = nil;
}
//上鎖
SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);
//校驗季春,如果舊值對不上 goto retry
if (haveOld && *location != oldObj) {
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
goto retry;
}
//保證弱引用對象的isa非空洗搂,防止弱引用機制和+initialize 發(fā)生死鎖
if (haveNew && newObj) {
Class cls = newObj->getIsa();
if (cls != previouslyInitializedClass &&
!((objc_class *)cls)->isInitialized())
{
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
//如果class沒有初始化發(fā)送+initialized消息
class_initialize(cls, (id)newObj);
previouslyInitializedClass = cls;
//到這里class肯定已經(jīng)初始化了,在走一遍
goto retry;
}
}
if (haveOld) {
//<<1>>如果weak 指針之前指向了其他對象载弄,在這里清空
weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
}
if (haveNew) {
//通過newObj和location生成一個新的weak_entry_t并插入到newObj的弱引用數(shù)組中(weak_entries)
//<<2>>
newObj = (objc_object *)
weak_register_no_lock(&newTable->weak_table, (id)newObj, location,
crashIfDeallocating);
// Set is-weakly-referenced bit in refcount table.
if (newObj && !newObj->isTaggedPointer()) {
//<<3>> 設(shè)置 isa 的標(biāo)志位
newObj->setWeaklyReferenced_nolock();
}
// Do not set *location anywhere else. That would introduce a race.
*location = (id)newObj;
}
else {
// No new value. The storage is not changed.
}
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
return (id)newObj;
}
4.1 解除 weak 指針之前舊的指向
方法調(diào)用如下:
// 參數(shù) 1:weak 指針指向?qū)ο笏诘娜止1?// 參數(shù) 2:weak 指針指向的舊對象
// 參數(shù) 3:weak 指針的地址
weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
weak_unregister_no_lock
方法主要是清除存儲在weak_entry_t
中的weak_refrerrer_t
耘拇,如果刪除后weak_entry_t
中的數(shù)組為空,則將整個weak_entry_t
從weak_table_t
中移除宇攻, 方法源碼如下:
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_ptr的地址
weak_entry_t *entry;
if (!referent) return;
//<<1>> 查找referent對應(yīng)的weak_entry_t
if ((entry = weak_entry_for_referent(weak_table, referent))) {
//<<2>>如果entry存在惫叛,刪除entry中的referrer
remove_referrer(entry, referrer);
bool empty = true;
// 判斷entry的動態(tài)數(shù)組referrers中是否有值
if (entry->out_of_line() && entry->num_refs != 0) {
empty = false;
}
else {
//判斷entry的定長數(shù)組inline_referrers中是否有值
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
if (entry->inline_referrers[i]) {
empty = false;
break;
}
}
}
//如果都是空的將entry從weak_table移除
if (empty) {
//<<3>>移除entry
weak_entry_remove(weak_table, entry);
}
}
4.1.1 查找referent對應(yīng)的weak_entry_t
具體方法實現(xiàn)如下:
// weak_table 當(dāng)前對象對應(yīng)的SideTable中的弱引用表
// referent 當(dāng)前對象
static weak_entry_t *
weak_entry_for_referent(weak_table_t *weak_table, objc_object *referent)
{
ASSERT(referent);
// 獲取所有的weak_entry_t數(shù)組
weak_entry_t *weak_entries = weak_table->weak_entries;
if (!weak_entries) return nil;
// 通過對對象指針的哈希方法生成的值與 weak_table->mask 進行 BITMASK 操作得到一個起始值
size_t begin = hash_pointer(referent) & weak_table->mask;
size_t index = begin;
size_t hash_displacement = 0;
while (weak_table->weak_entries[index].referent != referent) {
index = (index+1) & weak_table->mask;
if (index == begin) bad_weak_table(weak_table->weak_entries);
hash_displacement++;
if (hash_displacement > weak_table->max_hash_displacement) {
return nil;
}
}
return &weak_table->weak_entries[index];
}
- 每次遍歷如果沒有在
weak_entries
中找到referent
對應(yīng)的weak_entry_t
,就對index + 1
再進行BITMASK
操作逞刷,遍歷一次就記錄一次嘉涌,直到大于max_hash_displacement
最大偏移值,返回 nil夸浅,說明當(dāng)前對象在weak_table
的weak_entries
中沒有對應(yīng)的weak_entry_t
仑最,也就是說沒有弱引用 - 如果某個
weak_entry_t
的referent
和參數(shù)referent
相等,說明找到了帆喇,返回這個weak_entry_t
4.1.2 刪除 entry 中的 referrer
在上一步中我們找到了存儲當(dāng)前對象弱引用的weak_entry_t
警医,現(xiàn)在我們就需要從weak_entry_t
中的referrers
或者inline_referrers
中刪除掉之前的弱引用,源碼實現(xiàn)如下:
// entry: 當(dāng)前對象對應(yīng)的 weak_entry_t
// old_referrer : 弱引用指針的地址
static void remove_referrer(weak_entry_t *entry, objc_object **old_referrer)
{
if (! entry->out_of_line()) {
// 從 entry 的定長(長度為 4)數(shù)組inline_referrers中查找 old_referrer坯钦,如果有則置為 nil
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;
}
// 從動態(tài)數(shù)組中查找
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;
}
}
// 置為 nil预皇,并且對應(yīng)的數(shù)量-1
entry->referrers[index] = nil;
entry->num_refs--;
}
- 如果使用的是定長數(shù)組(數(shù)量為 4 個),那么就對
inline_referrers
進行遍歷查找婉刀,如果存在弱引用指針old_referrer
吟温,則設(shè)為 nil - 如果使用的是動態(tài)數(shù)組,那么就對
referrers
進行查找路星,如果存在溯街,設(shè)為nil,并將引用數(shù)量-1洋丐,查找方法和上面??那一步類似
4.1.3 如果 entry中的數(shù)組為空呈昔,則從 weak_table_t中刪除 entry
源碼實現(xiàn)如下:
static void weak_entry_remove(weak_table_t *weak_table, weak_entry_t *entry)
{
// 如果使用的是動態(tài)數(shù)組,則釋放動態(tài)數(shù)組的內(nèi)存
if (entry->out_of_line()) free(entry->referrers);
// 以 entry 為起始地址的前sizeof(*entry)個字節(jié)區(qū)域清零
bzero(entry, sizeof(*entry));
// 全局 weak_table_t中的弱引用對象數(shù)量-1
weak_table->num_entries--;
// 收縮表大小
weak_compact_maybe(weak_table);
}
- weak_compact_maybe(weak_table) 收縮表大小
實現(xiàn)如下:
static void weak_compact_maybe(weak_table_t *weak_table)
{
// #define TABLE_SIZE(entry) (entry->mask ? entry->mask + 1 : 0)
size_t old_size = TABLE_SIZE(weak_table);
// 如果 weak_table 的表大小占用超過 1024 個字節(jié)友绝,并且 1/16比弱引用的對象的數(shù)量還多就收縮表的大小堤尾,使其不超過原來的 1/2
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
}
}
- weak_resize源碼
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;
// 使用新的大小創(chuàng)建新的new_entries
weak_entry_t *new_entries = (weak_entry_t *)
calloc(new_size, sizeof(weak_entry_t));
// 將weak_table的成員變量重新賦值
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
// 如果old_entries還有值,則進行遍歷重新放入 weak_table 新的weak_entries中
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);
}
}
4.2 生成新的 weak_entry_t 插入到weak_table_t中的 weak_entryies中
之前storeWeak
中相關(guān)的代碼如下:
...
newObj = (objc_object *)
weak_register_no_lock(&newTable->weak_table, (id)newObj, location,
crashIfDeallocating);
...
我們具體來看下weak_register_no_lock
方法的源碼迁客,實現(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;
// 如果為 nil郭宝,或者是TaggedPointer辞槐,則直接 return referent_id
if (!referent || referent->isTaggedPointer()) return referent_id;
// 判斷當(dāng)前對象是否正在釋放或是否支持弱引用
bool deallocating;
if (!referent->ISA()->hasCustomRR()) {
deallocating = referent->rootIsDeallocating();
}
else {
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));
}
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;
}
}
weak_entry_t *entry;
// 判斷如果對象已經(jīng)在 weak_table 中存在弱引用記錄,就在原來的 entry 上追加
if ((entry = weak_entry_for_referent(weak_table, referent))) {
append_referrer(entry, referrer);
}
// 如果不存在則創(chuàng)建一個新的 entry粘室,加入到 weak_table中
else {
weak_entry_t new_entry(referent, referrer);
weak_grow_maybe(weak_table);
weak_entry_insert(weak_table, &new_entry);
}
return referent_id;
}
4.2.1 weak_entry_t 中添加新的 weak_referrer_t
append_referrer
具體實現(xiàn)如下:
static void append_referrer(weak_entry_t *entry, objc_object **new_referrer)
{
// 如果使用的是定長數(shù)組榄檬,找到為 nil 的位置,賦值即可
if (! entry->out_of_line()) {
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
if (entry->inline_referrers[i] == nil) {
entry->inline_referrers[i] = new_referrer;
return;
}
}
// 如果放不下了衔统,就申請創(chuàng)建動態(tài)數(shù)組new_referrers
weak_referrer_t *new_referrers = (weak_referrer_t *)
calloc(WEAK_INLINE_COUNT, sizeof(weak_referrer_t));
// 然后將之前定長數(shù)組的弱引用賦值給new_referrers
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
new_referrers[i] = entry->inline_referrers[i];
}
// 設(shè)置 entry 相關(guān)的變量的值
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;
}
ASSERT(entry->out_of_line());
// 如果引用數(shù)量超過表大小的 3/4就自動擴容
if (entry->num_refs >= TABLE_SIZE(entry) * 3/4) {
return grow_refs_and_insert(entry, new_referrer);
}
// 在 referrers 中找到一個值為 nil 的 weak_referrer_t鹿榜,用新的弱引用對其賦值,并將引用數(shù)量+1
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++;
}
- grow_refs_and_insert 對某個對象的弱引用表擴容并進行插入
__attribute__((noinline, used))
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);
// 如果之前有舊表锦爵,則擴容為之前的 2 倍舱殿,否則為 8
size_t new_size = old_size ? old_size * 2 : 8;
// 獲取當(dāng)前對象所有弱引用指針的數(shù)量
size_t num_refs = entry->num_refs;
// 獲取舊的數(shù)組
weak_referrer_t *old_refs = entry->referrers;
// mask 賦值
entry->mask = new_size - 1;
// 使用新的 size 創(chuàng)建referrers,并給 entry 賦值
entry->referrers = (weak_referrer_t *)
calloc(TABLE_SIZE(entry), sizeof(weak_referrer_t));
// 剛開始都為 0
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--;
}
}
// 最后將新的弱引用指針地址進行插入
append_referrer(entry, new_referrer);
if (old_refs) free(old_refs);
}
4.2.2 new_entry(referent, referrer) 創(chuàng)建新的 entry
構(gòu)造方法實現(xiàn)如下:
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;
}
}
從上面代碼中可以看出险掀,剛開始對象沒有弱引用指針沪袭,使用的是定長數(shù)組inline_referrers
4.2.3 weak_grow_maybe weak_table擴容
實現(xiàn)代碼如下:
static void weak_grow_maybe(weak_table_t *weak_table)
{
size_t old_size = TABLE_SIZE(weak_table);
// 如果超過 3/4 則進行擴容,如果之前有樟氢,則為原來的 2 倍冈绊,否則為 64
if (weak_table->num_entries >= old_size * 3 / 4) {
weak_resize(weak_table, old_size ? old_size*2 : 64);
}
}
這里也是調(diào)用weak_resize
方法,將weak_table_t
進行擴容嗡害,上面??是進行收縮焚碌。
4.2.4 weak_table_t 中插入 weak_entry_t
實現(xiàn)代碼如下:
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++;
}
// 進行賦值畦攘,數(shù)量自增
weak_entries[index] = *new_entry;
weak_table->num_entries++;
// 對最大max_hash_displacement偏移值進行賦值霸妹,這也是查找時遍歷的臨界點
if (hash_displacement > weak_table->max_hash_displacement) {
weak_table->max_hash_displacement = hash_displacement;
}
}
4.3 設(shè)置弱引用的標(biāo)志位
這一步就是標(biāo)記這個對象有弱引用,在這個對象 dealloc
的時候知押,將所有的弱引用釋放掉叹螟,實現(xiàn)代碼如下:
inline void
objc_object::setWeaklyReferenced_nolock()
{
retry:
// 獲取對象的 isa 指針
isa_t oldisa = LoadExclusive(&isa.bits);
isa_t newisa = oldisa;
if (slowpath(!newisa.nonpointer)) {
ClearExclusive(&isa.bits);
sidetable_setWeaklyReferenced_nolock();
return;
}
// 如果對象isa 的弱引用標(biāo)志位已經(jīng)是 true 了,則不進行操作
if (newisa.weakly_referenced) {
ClearExclusive(&isa.bits);
return;
}
// 弱引用標(biāo)志位設(shè)為 true
newisa.weakly_referenced = true;
// //如果oldisa.bits和newisa.bits不相等返回NO台盯,繼續(xù)tery里面的內(nèi)容罢绽,這時候newisa.weakly_referenced已經(jīng)是true了,所以return
if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;
}
5. 弱引用指針的釋放
- (void)dealloc {
_objc_rootDealloc(self);
}
?
void _objc_rootDealloc(id obj)
{
ASSERT(obj);
obj->rootDealloc();
}
?
inline void objc_object::rootDealloc()
{
if (isTaggedPointer()) return; // fixme necessary?
if (fastpath(isa.nonpointer &&
!isa.weakly_referenced &&
!isa.has_assoc &&
!isa.has_cxx_dtor &&
!isa.has_sidetable_rc))
{
assert(!sidetable_present());
free(this);
}
else {
object_dispose((id)this);
}
}
?
id object_dispose(id obj)
{
if (!obj) return nil;
objc_destructInstance(obj);
free(obj);
return 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.
if (cxx) object_cxxDestruct(obj);
if (assoc) _object_remove_assocations(obj);
obj->clearDeallocating();
}
return obj;
}
?
inline void
objc_object::clearDeallocating()
{
if (slowpath(!isa.nonpointer)) {
// Slow path for raw pointer isa.
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.
clearDeallocating_slow();
}
assert(!sidetable_present());
}
?
NEVER_INLINE void
objc_object::clearDeallocating_slow()
{
ASSERT(isa.nonpointer && (isa.weakly_referenced || isa.has_sidetable_rc));
SideTable& table = SideTables()[this];
table.lock();
if (isa.weakly_referenced) {
weak_clear_no_lock(&table.weak_table, (id)this);
}
if (isa.has_sidetable_rc) {
table.refcnts.erase(this);
}
table.unlock();
}
終于來到了關(guān)鍵的方法weak_clear_no_lock
静盅,實現(xiàn)如下:
*/
void
weak_clear_no_lock(weak_table_t *weak_table, id referent_id)
{
objc_object *referent = (objc_object *)referent_id;
// 獲取當(dā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;
if (entry->out_of_line()) {
referrers = entry->referrers;
count = TABLE_SIZE(entry);
}
else {
referrers = entry->inline_referrers;
count = WEAK_INLINE_COUNT;
}
// 遍歷entry的 referrers 數(shù)組良价,將弱引用指針全置為 nil
for (size_t i = 0; i < count; ++i) {
objc_object **referrer = referrers[i];
if (referrer) {
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中移除當(dāng)前對象對應(yīng)的 entry
weak_entry_remove(weak_table, entry);
}
上面流程主要是針對于weak
實現(xiàn)的介紹,其中對于 isa.nonpointer
還有 isa
的標(biāo)志位以及 Tagged Pointer
沒有進行過多介紹蒿叠,之后文章會詳細進行介紹明垢。