一、weak的作用
這里的weak包括屬性關(guān)鍵字weak和__weak兩種:__weak用于修飾變量(variable),weak用于修飾屬性(property)。
1. weak是弱引用,用weak描述修飾或者所引用對(duì)象的計(jì)數(shù)不會(huì)加1
2. weak修飾的指針在引用的對(duì)象被釋放的時(shí)候自動(dòng)被設(shè)置為nil,避免野指針
3. weak可以解決使用block引起的循環(huán)引用。
看到這里又想起了block和assign,復(fù)習(xí)一下這幾個(gè)的區(qū)別:
1.__block不管是ARC還是MRC模式下都可以使用,可以修飾對(duì)象,還可以修飾基本數(shù)據(jù)類型。
2.__weak只能在ARC模式下使用,也只能修飾對(duì)象(NSString),不能修飾基本數(shù)據(jù)類型(int)。assign用來修飾基本數(shù)據(jù)類型
3.__block對(duì)象可以在block中被重新賦值,__weak不可以。
4.__block對(duì)象在ARC下可能會(huì)導(dǎo)致循環(huán)引用,非ARC下會(huì)避免循環(huán)引用,__weak只在ARC下使用,可以避免循環(huán)引用。
OK,以上說的都沒有錯(cuò),大家也都會(huì)用,但是你知道是怎么實(shí)現(xiàn)的嗎
二、weak的源碼實(shí)現(xiàn)
1、先看下objc-weak.h文件里面的API
typedef DisguisedPtr<objc_object *> weak_referrer_t;
struct weak_entry_t { ... };
struct weak_table_t { ... };
/// 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);
看起來很簡(jiǎn)單的結(jié)構(gòu)體和幾個(gè)方法:
- weak_table_t:翻譯一下注釋上的說明,弱引用的哈希表table,對(duì)象id作為key,weak_entry_t結(jié)構(gòu)體作為value
- weak_register_no_lock:向weak table里面添加一個(gè)object和它的weak引用指針
- weak_unregister_no_lock:從weak table移除一個(gè)object和它的weak引用指針
- weak_is_registered_no_lock:如果object有弱引用返回true
- weak_clear_no_lock:object對(duì)象被銷毀,把所有指向它的弱引用指針全部置為nil
2、根據(jù)API看下.m里面的實(shí)現(xiàn)
2.1 weak_register_no_lock
添加弱引用
源碼實(shí)現(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;
// ensure that the referenced object is viable
。。。這里省略判斷被引用的對(duì)象是否存在的代碼,只看關(guān)鍵部分
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;
}
解釋一下關(guān)鍵地方:
-
if ((entry = weak_entry_for_referent(weak_table, referent)))
:判斷weak_table是否存在這個(gè)對(duì)象referent - 如果table表中已經(jīng)存在對(duì)象referent,那么執(zhí)行
append_referrer(entry, referrer);
把referrer這個(gè)新的引用加入到referent已經(jīng)存在的引用列表中 - 如果不存在,執(zhí)行
weak_entry_t new_entry(referent, referrer)
:給對(duì)象referent創(chuàng)建一個(gè)新的引用列表
weak_grow_maybe(weak_table)
:weak_table增加內(nèi)存
weak_entry_insert(weak_table, &new_entry)
:把referent的引用列表加入到weak_table中
2.2 weak_unregister_no_lock
:移除弱引用
當(dāng)我們使用weak指針指向一個(gè)對(duì)象__weak typeof(self) = self
的時(shí)候,會(huì)調(diào)weak_register_no_lock
在weak table表中添加這個(gè)引用,但是移除引用什么時(shí)候會(huì)用呢,還是看一下源碼和注釋:
/**
* Unregister an already-registered weak reference.
* This is used when referrer's storage is about to go away, but referent
* isn't dead yet. (Otherwise, zeroing referrer later would be a
* bad memory access.)
* Does nothing if referent/referrer is not a currently active weak reference.
* Does not zero referrer.
*/
void
weak_unregister_no_lock(weak_table_t *weak_table, id referent_id,
id *referrer_id)
{
// referent是對(duì)象也就是weak table中的key
objc_object *referent = (objc_object *)referent_id;
//*referrer是指向這個(gè)對(duì)象的弱引用指針
objc_object **referrer = (objc_object **)referrer_id;
weak_entry_t *entry;
if (!referent) return;
///從weak_table中根據(jù)key(referent)找到entry(指向?qū)ο笕跻昧斜淼闹羔槪? if ((entry = weak_entry_for_referent(weak_table, referent))) {
/// 從引用列表中移除這個(gè)引用referrer, remove_referrer這個(gè)方法會(huì)把這個(gè)referrer置為nil
remove_referrer(entry, referrer);
/// 之后遍歷對(duì)象referent的引用列表是不是空了
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) {
/// 如果該對(duì)象的弱引用列表已經(jīng)empty,就把該對(duì)象從weak_table中移除
weak_entry_remove(weak_table, entry);
}
}
}
這個(gè)應(yīng)該是這種情況下使用的,比如我們?cè)谝粋€(gè)方法里面這樣定義:
- (void)testWeakReferrer {
__weak typeof(self) weakSelf = self;
...
return;
}
weakSelf是方法的局部變量,當(dāng)方法執(zhí)行完的時(shí)候,局部變量weakSelf會(huì)被銷毀,這個(gè)時(shí)候系統(tǒng)應(yīng)該就會(huì)自動(dòng)執(zhí)行weak_unregister_no_lock方法,把weakSelf從self對(duì)象的弱引用列表中移除,并且把weakSelf置為nil。
2.3 weak_clear_no_lock
清空所有的weak指針
/**
* 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)
{
objc_object *referent = (objc_object *)referent_id;
weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
...
// zero out references
weak_referrer_t *referrers;
size_t count;
...//獲取列表中的數(shù)量
for (size_t i = 0; i < count; ++i) {
objc_object **referrer = referrers[i];
if (referrer) {
if (*referrer == referent) {
*referrer = nil;
}
...
}
}
weak_entry_remove(weak_table, entry);
}
Called by dealloc,當(dāng)對(duì)象被銷毀的時(shí)候調(diào)用這個(gè)方法,在這個(gè)方法里面把所有指向這個(gè)對(duì)象的弱引用指針全部置為nil,最后從weak table中移除該對(duì)象和它的弱引用列表。
ok,這也就解釋了為什么weak指針會(huì)被自動(dòng)置為nil了