append_referrer
将referrer插入到weak_entry_t中
/** * Add the given referrer to set of weak pointers in this entry. * Does not perform duplicate checking (b/c weak pointers are never * added to a set twice). * * @param entry The entry holding the set of weak pointers. * @param new_referrer The new weak pointer to be added. */ 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; } ASSERT(entry->out_of_line()); if (entry->num_refs >= TABLE_SIZE(entry) * 3/4) { return grow_refs_and_insert(entry, new_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++; }
grow_refs_and_insert
如果动态数组中元素个数大于或等于数组总空间的3/4,则扩展数组空间为当前长度的一倍,然后将 referrer 插入数组
/** * Grow the entry's hash table of referrers. Rehashes each * of the referrers. * * @param entry Weak pointer hash set for a particular object. */ __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); 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); }
weak_unregister_no_lock
如果weak指针在指向obj之前,已经弱引用了其他的对象,则需要先将weak指针从其他对象的weak_entry_t的hash数组中移除。在storeWeak方法中会调用weak_unregister_no_lock函数来做移除操作
/** * 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. * * FIXME currently requires old referent value to be passed in (lame) * FIXME unregistration should be automatic if referrer is collected * * @param weak_table The global weak table. * @param referent The object. * @param referrer The weak reference. */ 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. }
2. weak对象销毁过程
_objc_rootDealloc
// Replaced by NSZombies - (void)dealloc { _objc_rootDealloc(self); }
void _objc_rootDealloc(id obj) { ASSERT(obj); obj->rootDealloc(); }
rootDealloc
inline void objc_object::rootDealloc() { //如果是Tagged Pointer,就直接返回 if (isTaggedPointer()) return; // fixme necessary? /* 如果同时满足 1. 是优化过的isa、 2. 没有被weak指针引用过、 3. 没有关联对象、 4. 没有C++析构函数、 5. 没有sideTable, 就可以直接释放内存free() */ if (fastpath(isa.nonpointer && !isa.weakly_referenced && !isa.has_assoc && #if ISA_HAS_CXX_DTOR_BIT !isa.has_cxx_dtor && #else !isa.getClass(false)->hasCxxDtor() && #endif !isa.has_sidetable_rc)) { assert(!sidetable_present()); free(this); } else { object_dispose((id)this); } }
object_dispose
id object_dispose(id obj) { if (!obj) return nil; objc_destructInstance(obj); free(obj); return nil; }
objc_destructInstance
/*********************************************************************** * objc_destructInstance * Destroys an instance without freeing memory. * Calls C++ destructors. * Removes associative references. * Returns `obj`. Does nothing if `obj` is nil. * CoreFoundation and other clients do call this under GC. **********************************************************************/ void *objc_destructInstance(id obj) { if (obj) { Class isa = obj->getIsa(); if (isa->hasCxxDtor()) { object_cxxDestruct(obj); } if (isa->instancesHaveAssociatedObjects()) { _object_remove_assocations(obj); } objc_clear_deallocating(obj); } return obj; }
objc_clear_deallocating
void objc_clear_deallocating(id obj) { ASSERT(obj); if (obj->isTaggedPointer()) return; obj->clearDeallocating(); }
clearDeallocating
inline void objc_object::clearDeallocating() { if (slowpath(!isa.nonpointer)) { // Slow path for raw pointer isa. //如果要释放的对象没有采用了优化过的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. //如果要释放的对象采用了优化过的isa引用计数,并且有弱引用或者使用了sideTable的辅助引用计数 clearDeallocating_slow(); } assert(!sidetable_present()); }
clearDeallocating_slow
// Slow path of clearDeallocating() // for objects with nonpointer isa // that were ever weakly referenced // or whose retain count ever overflowed to the side table. NEVER_INLINE void objc_object::clearDeallocating_slow() { ASSERT(isa.nonpointer && (isa.weakly_referenced || isa.has_sidetable_rc)); //在全局的SideTables中,以this指针(要释放的对象)为key,找到对应的SideTable SideTable& table = SideTables()[this]; table.lock(); if (isa.weakly_referenced) { //要释放的对象被弱引用了,通过weak_clear_no_lock函数将指向该对象的弱引用指针置为nil weak_clear_no_lock(&table.weak_table, (id)this); } //使用了sideTable的辅助引用计数,直接在SideTable中擦除该对象的引用计数 if (isa.has_sidetable_rc) { table.refcnts.erase(this); } table.unlock(); }
weak_clear_no_lock
/** * 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); 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; } 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); }