讀weak之前先了解三個(gè)數(shù)據(jù)結(jié)構(gòu):SideTable盐肃、weak_table_t腥沽、weak_entry_t
一申眼、基本數(shù)據(jù)結(jié)構(gòu)
1.SideTable結(jié)構(gòu)體
管理著引用計(jì)數(shù)表與弱引用表
struct SideTable {
spinlock_t slock;
RefcountMap refcnts;//引用計(jì)數(shù)表
weak_table_t weak_table;//弱引用表
...
};
2.weak_table_t結(jié)構(gòu)體
全局弱引用表划煮。將對(duì)象id存儲(chǔ)為鍵,和weak_entry_t結(jié)構(gòu)作為它們的值。
struct weak_table_t {
weak_entry_t *weak_entries;// hash數(shù)組磕秤,用來(lái)存儲(chǔ)弱引用對(duì)象的相關(guān)信息weak_entry_t
size_t num_entries;// hash數(shù)組中的元素個(gè)數(shù)
uintptr_t mask;// hash數(shù)組長(zhǎng)度-1筑累,會(huì)參與hash計(jì)算。(注意逻炊,這里是hash數(shù)組的長(zhǎng)度互亮,而不是元素個(gè)數(shù)。比如余素,數(shù)組長(zhǎng)度可能是64豹休,而元素個(gè)數(shù)僅存了2個(gè))
uintptr_t max_hash_displacement;// 可能會(huì)發(fā)生的hash沖突的最大次數(shù)
};
3. weak_entry_t
靜態(tài)數(shù)組與動(dòng)態(tài)數(shù)組結(jié)合的結(jié)構(gòu)體,數(shù)量小于等于4存靜態(tài)數(shù)組中桨吊,大于4存動(dòng)態(tài)數(shù)組
struct weak_entry_t {
DisguisedPtr<objc_object> referent;// 被弱引用的對(duì)象
// 引用該對(duì)象的對(duì)象列表威根,聯(lián)合。 引用個(gè)數(shù)小于4视乐,用inline_referrers數(shù)組洛搀。 用個(gè)數(shù)大于4,用動(dòng)態(tài)數(shù)組weak_referrer_t *referrers
union {
struct {
weak_referrer_t *referrers;// 弱引用該對(duì)象的對(duì)象列表的動(dòng)態(tài)數(shù)組
uintptr_t out_of_line_ness : 2;// 是否使用動(dòng)態(tài)數(shù)組標(biāo)記位
uintptr_t num_refs : PTR_MINUS_2;// 動(dòng)態(tài)數(shù)組中有效元素個(gè)數(shù)
uintptr_t mask;//動(dòng)態(tài)數(shù)組元素個(gè)數(shù)
uintptr_t max_hash_displacement;// 最大的hash沖突次數(shù)(說(shuō)明了最多做max_hash_displacement次hash沖突佑淀,肯定會(huì)找到對(duì)應(yīng)的數(shù)據(jù))
};
struct {
// out_of_line_ness field is low bits of inline_referrers[1]
weak_referrer_t inline_referrers[WEAK_INLINE_COUNT];
};
};
...
};
二留美、初始化
1.入口
//weak源碼分析
void weak(){
Person * person = [[Person alloc] init];
//指針地址指向指針
__weak Person *weakPerson = person;
__weak Person *weakPerson2 = person;
NSLog(@"person指針:%p\nweakPerson指針:%p",person,weakPerson);
NSLog(@"person指針地址:%p\nweakPerson指針地址:%p",&person,&weakPerson);
}
流程:objc_initWeak(id *location, id newObj)->storeWeak(id *location, objc_object *newObj)->weak_register_no_lock(...)
2.weak_register_no_lock
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;//對(duì)象指針
objc_object **referrer = (objc_object **)referrer_id;//指針地址
// 如果referent為nil 或 referent 采用了TaggedPointer計(jì)數(shù)方式,直接返回渣聚,不做任何操作
if (!referent || referent->isTaggedPointer()) return referent_id;
// ensure that the referenced object is viable
// 確保被引用的對(duì)象可用(沒(méi)有在析構(gòu)独榴,同時(shí)應(yīng)該支持weak引用)
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);
}
// 正在析構(gòu)的對(duì)象,不能夠被弱引用
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;
}
}
// now remember it and where it is being stored
// 在 weak_table中找到referent對(duì)應(yīng)的weak_entry,并將referrer加入到weak_entry中
weak_entry_t *entry;
if ((entry = weak_entry_for_referent(weak_table, referent))) {// 如果能找到weak_entry,則講referrer插入到weak_entry中
append_referrer(entry, referrer);// 將referrer追加到weak_entry_t的引用數(shù)組中
}
else {
weak_entry_t new_entry(referent, referrer);//創(chuàng)建weak_entry_t
weak_grow_maybe(weak_table);//擴(kuò)大數(shù)組操作
weak_entry_insert(weak_table, &new_entry);//new_entry插入weak_table
}
return referent_id;
}
- 對(duì)象指針不能為空奕枝,非TaggedPointer對(duì)象棺榔。
- 判斷對(duì)象沒(méi)有析構(gòu)
- 最主要是最后面一段:
1.分配一個(gè)新的new_entry對(duì)象
2.weak_grow_maybe判斷weak_table是否需要擴(kuò)容
3.new_entry插入到weak_table中
4.如果weak_table中有referent,用append_referrer追加到entry中隘道。
3.weak_entry_for_referent(weak_table, referent)
查找weak_table是否包含了referent症歇。一段while循環(huán)查找。
static weak_entry_t *
weak_entry_for_referent(weak_table_t *weak_table, objc_object *referent)
{
assert(referent);
weak_entry_t *weak_entries = weak_table->weak_entries;
if (!weak_entries) return nil;
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];
}
4.weak_grow_maybe
判斷weak_table是否需要擴(kuò)容操作谭梗,當(dāng)實(shí)際個(gè)數(shù)>=總長(zhǎng)度的3/4時(shí)忘晤,在原有基礎(chǔ)上擴(kuò)大2倍。
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.
if (weak_table->num_entries >= old_size * 3 / 4) {// 當(dāng)大于現(xiàn)有長(zhǎng)度的3/4時(shí)激捏,會(huì)做數(shù)組擴(kuò)容操作设塔。
weak_resize(weak_table, old_size ? old_size*2 : 64);// 初次會(huì)分配64個(gè)位置,之后在原有基礎(chǔ)上*2
}
}
5.weak_entry_insert把new_entry插入到weak_table_t中远舅。
三.銷(xiāo)毀
流程:objc_destroyWeak->storeWeak->weak_unregister_no_lock->remove_referrer
1.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;
// 如果referent為nil 或 referent 采用了TaggedPointer計(jì)數(shù)方式闰蛔,直接返回痕钢,不做任何操作
if (!referent) return;
if ((entry = weak_entry_for_referent(weak_table, referent))) {// 查找
remove_referrer(entry, referrer);// 在referent所對(duì)應(yīng)的weak_entry_t的hash數(shù)組中,移除referrer
// 移除元素之后序六, 要檢查一下weak_entry_t的hash數(shù)組是否已經(jīng)空了
bool empty = true;
//使用了可變數(shù)組任连,且數(shù)量不為0
if (entry->out_of_line() && entry->num_refs != 0) {
empty = false;
}
else {
//靜態(tài)數(shù)組有值
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
if (entry->inline_referrers[i]) {
empty = false;
break;
}
}
}
if (empty) {// 如果weak_entry_t的hash數(shù)組已經(jīng)空了,則需要將weak_entry_t從weak_table中移除
weak_entry_remove(weak_table, entry);
}
}
}
- weak_entry_for_referent在weak_table查找referent例诀,entry不為空做移除
- remove_referrer從weak_table移除referrer
- out_of_line動(dòng)態(tài)數(shù)組標(biāo)識(shí)與靜態(tài)數(shù)組如果還不為空随抠,weak_entry_remove移除entry
2.remove_referrer
static void remove_referrer(weak_entry_t *entry, objc_object **old_referrer)
{
if (! entry->out_of_line()) {//靜態(tài)數(shù)組
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;
}
//動(dòng)態(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;
}
}
entry->referrers[index] = nil;
entry->num_refs--;
}
如果是靜態(tài)數(shù)組,置空后直接return.否則動(dòng)態(tài)數(shù)組釋放
3.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));//清理entry sizeof(*entry)個(gè)字節(jié)
weak_table->num_entries--;
weak_compact_maybe(weak_table);//收容
}
釋放繁涂,收容拱她。
4.釋放
局部變量在方法結(jié)束后立即釋放,全局變量會(huì)在dealloc方法是否爆土。
總結(jié):
- weak的使用靜態(tài)與動(dòng)態(tài)數(shù)組提高了效率
- 動(dòng)態(tài)數(shù)組椭懊,實(shí)質(zhì)在插入數(shù)據(jù)時(shí)判斷實(shí)際個(gè)數(shù)與總個(gè)數(shù)的大小,判斷是否需要擴(kuò)容步势。釋放時(shí)也是如此來(lái)達(dá)到收容氧猬。
- 對(duì)于dealloc中的釋放,會(huì)根據(jù)弱引用標(biāo)志位weakly_referenced調(diào)用weak_clear_no_lock進(jìn)行釋放坏瘩。