weak到底做了些啥? 入行也好几年了,关于内存管理几乎是面试中必定会问到的问题,内存管理中问的最多的大概就是循环引用和weak的实现了,关于weak怎么将修饰的对象置为nil的,可能几年前的回答就是:
1 runtime维护了一个weak表,用于存储指向某个对象的所有weak指针。weak表其实是一个hash(哈希)表,Key是所指对象的地址,Value是weak指针的地址(这个地址的值是所指对象指针的地址)数组。
再之后可能就是weak的三部曲:
1 2 3 1、初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。 2、添加引用时:objc_initWeak函数会调用 objc_storeWeak() 函数, objc_storeWeak() 的作用是更新指针指向,创建对应的弱引用表。 3、释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。
那么上面提及到的函数到底都做了些啥?
objc_initWeak 1 2 3 4 5 6 7 8 9 10 11 enum HaveOld { DontHaveOld = false , DoHaveOld = true };enum HaveNew { DontHaveNew = false , DoHaveNew = true };id objc_initWeak (id *location, id newObj) { if (!newObj) { *location = nil; return nil; } return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>(location, (objc_object*)newObj); }
从上面的代码块,我们可以知道objc_initWeak
其实是objc_storeWeak
的入口,他主要判断了修饰的对象是否有效,倘若是个无效对象,则直接将指针置为nil,返回nil。
storeWeak 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 enum CrashIfDeallocating { DontCrashIfDeallocating = false , DoCrashIfDeallocating = true }; 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) { 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; } 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)); previouslyInitializedClass = cls; goto retry; } } if (haveOld) { weak_unregister_no_lock(&oldTable->weak_table, oldObj, location); } if (haveNew) { newObj = (objc_object *)weak_register_no_lock(&newTable->weak_table, (id)newObj, location, crashIfDeallocating); if (newObj && !newObj->isTaggedPointer()) { newObj->setWeaklyReferenced_nolock(); } *location = (id)newObj; } SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable); return (id)newObj; }
从以上代码可以看出storeWeak
先是做了一系列的条件判断以及保护操作,之后对新值旧值进行了操作,简而言之就是清除旧值分配新值。
但是,这个SideTable
是干啥的?
1 2 3 4 5 6 7 8 struct SideTable { spinlock_t slock; RefcountMap refcnts; weak_table_t weak_table; }
这个结构体包含了引用计数表和weak表,想必便是用于管理对象的引用计数和weak表的。
weak_unregister_no_lock 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 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); } } }
storeWeak
中清除旧值的操作调用了weak_unregister_no_lock()
,该函数最后会调用weak_entry_remove()
将其从弱引用表中移除。 该函数首先判断了弱引用条目是否存在,然后判断对象是否处于活跃状态以及引用地址是否还存在,如果处于活跃状态或引用地址依然存在则不做任何操作,否则将该弱引用条目从弱引用表中清除。
weak_register_no_lock 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 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; 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; } } 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; }
该方法先是判断引用是否有效,引用对象是否处于销毁中的状态,最后再判断弱引用表是否存在referent,存在则直接添加弱引用,不存在则先创建weak_entry_t,然后对表进行扩容(如果需要的话),最后将弱引用插入表中。不得不说源码层面的条件判断非常严谨,各个会出现异常位置都有一个条件判断。
以上就是weak分配值的一系列操作了,还有一个问题就是对象调用dealloc
是怎么将弱引用变量置为nil的?
自动置为nil ok,我们都知道当一个对象引用计数为0的时候会调用dealloc
方法,那么以dealloc
为入口,去看看究竟都发生了一些什么事情。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 - (void )dealloc { _objc_rootDealloc(self); } void _objc_rootDealloc(id obj) { assert(obj); obj->rootDealloc(); } inline void objc_object::rootDealloc() { if (isTaggedPointer()) return ; 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) { bool cxx = obj->hasCxxDtor(); bool assoc = obj->hasAssociatedObjects(); if (cxx) object_cxxDestruct(obj); if (assoc) _object_remove_assocations(obj); obj->clearDeallocating(); } return obj; }
调用流程 dealloc -> rootDealloc -> object_dispose -> objc_destructInstance -> clearDeallocating
1 2 3 4 5 6 7 8 9 10 11 12 13 14 inline void objc_object::clearDeallocating() { if (slowpath(!isa.nonpointer)) { sidetable_clearDeallocating(); } else if (slowpath(isa.weakly_referenced || isa.has_sidetable_rc)) { clearDeallocating_slow(); } assert(!sidetable_present()); }
clearDeallocating
中的判断表示弱引用会进入clearDeallocating_slow()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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(); }
最终弱引用会调用到weak_clear_no_lock()
weak_clear_no_lock 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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); if (entry == nil) { return ; } 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; } 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_entry_remove(weak_table, entry); }
该方法获取了对象的所有弱引用变量,然后遍历弱引用数组,将这些变量分别置为nil。