/** * Return the weak reference table entry for the given referent. * If there is no entry for referent, return NULL. * Performs a lookup. * * @param weak_table * @param referent The object. Must not be nil. * * @return The table of weak referrers to this object. */ static weak_entry_t * weak_entry_for_referent(weak_table_t *weak_table, objc_object *referent) { ASSERT(referent);
// The address of a __weak variable. // These pointers are stored disguised so memory analysis tools // don't see lots of interior pointers from the weak table into objects. typedef DisguisedPtr<objc_object *> weak_referrer_t;
/** * The internal structure stored in the weak references table. * It maintains and stores * a hash set of weak references pointing to an object. * If out_of_line_ness != REFERRERS_OUT_OF_LINE then the set * is instead a small inline array. */ #define WEAK_INLINE_COUNT 4
// out_of_line_ness field overlaps with the low two bits of inline_referrers[1]. // inline_referrers[1] is a DisguisedPtr of a pointer-aligned address. // The low two bits of a pointer-aligned DisguisedPtr will always be 0b00 // (disguised nil or 0x80..00) or 0b11 (any other address). // Therefore out_of_line_ness == 0b10 is used to mark the out-of-line state. #define REFERRERS_OUT_OF_LINE 2
struct weak_entry_t { DisguisedPtr<objc_object> referent; union { 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; }; struct { // out_of_line_ness field is low bits of inline_referrers[1] weak_referrer_t inline_referrers[WEAK_INLINE_COUNT]; }; };
/** * 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. */ staticvoid 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; }
// 对 弱指针的指针 进行哈希计算,拿到索引,将对应的 弱指针 插入到 weak_referrer_t->referrers 中 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++; }
/** * Remove old_referrer from set of referrers, if it's present. * Does not remove duplicates, because duplicates should not exist. * * @todo this is slow if old_referrer is not present. Is this ever the case? * * @param entry The entry holding the referrers. * @param old_referrer The referrer to remove. */ staticvoid remove_referrer(weak_entry_t *entry, objc_object **old_referrer) { if (! entry->out_of_line()) { 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; }
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--; }
weak 指针的创建
objc_initWeak
1 2 3 4 5 6 7 8 9 10 11 12 13
// location: weak 指针地址,类型 id* // newObj: 所指向的对象, 类型 id id objc_initWeak(id *location, id newObj) { if (!newObj) { *location = nil; returnnil; }
// Update a weak variable. // If HaveOld is true, the variable has an existing value // that needs to be cleaned up. This value might be nil. // If HaveNew is true, there is a new value that needs to be // assigned into the variable. This value might be nil. // If CrashIfDeallocating is true, the process is halted if newObj is // deallocating or newObj's class does not support weak references. // If CrashIfDeallocating is false, nil is stored instead. enum CrashIfDeallocating { DontCrashIfDeallocating = false, DoCrashIfDeallocating = true }; template <HaveOld haveOld, HaveNew haveNew, CrashIfDeallocating crashIfDeallocating> staticid storeWeak(id *location, objc_object *newObj) { assert(haveOld || haveNew); if (!haveNew) assert(newObj == nil);
Class previouslyInitializedClass = nil; id oldObj; SideTable *oldTable; SideTable *newTable;
// Acquire locks for old and new values. // Order by lock address to prevent lock ordering problems. // Retry if the old value changes underneath us. retry:
// Prevent a deadlock between the weak reference machinery // and the +initialize machinery by ensuring that no // weakly-referenced object has an un-+initialized isa. if (haveNew && newObj) { Class cls = newObj->getIsa();
// If this class is finished with +initialize then we're good. // If this class is still running +initialize on this thread // (i.e. +initialize called storeWeak on an instance of itself) // then we may proceed but it will appear initializing and // not yet initialized to the check above. // Instead set previouslyInitializedClass to recognize it on retry. // 完成初始化后进行标记 previouslyInitializedClass = cls;
// newObj 初始化后,重新获取一遍newObj goto retry; } }
// Clean up old value, if any. // 如果 weak指针 之前弱引用过别的对象oldObj // weak_unregister_no_lock,移除 oldTable->weak_table->weak_entries 中该 weak指针 地址 if (haveOld) { weak_unregister_no_lock(&oldTable->weak_table, oldObj, location); }
// Assign new value, if any. // 如果 weak指针 需要弱引用新的对象 newObj // weak_register_no_lock,将 weak指针 的地址添加到 newObj 对应的 newTable->weak_table->weak_entries if (haveNew) { 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. // 更新 newObj 的 isa指针 的 weakly_referenced bit 标志位 if (newObj && !newObj->isTaggedPointer()) { newObj->setWeaklyReferenced_nolock(); }
// Do not set *location anywhere else. That would introduce a race. // *location 赋值,也就是将weak指针直接指向了newObj,而且没有将newObj的引用计数+1 *location = (id)newObj; } else { // No new value. The storage is not changed. }
// ensure that the referenced object is viable // 确保对象可用:没有在析构,支持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) { returnnil; } 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 { returnnil; } }
// now remember it and where it is being stored // 在 weak_table 中找到对象 referent 对应的 weak_entries,并将 referrer 加入到 weak_entries 中 weak_entry_t *entry; if ((entry = weak_entry_for_referent(weak_table, referent))) {
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. // c++ 析构方法 if (cxx) object_cxxDestruct(obj); // 移除关联对象,并将其自身从Association Manager的map中移除 if (assoc) _object_remove_assocations(obj); // 执行 clearDeallocating obj->clearDeallocating(); }
return obj; }
clearDeallocating
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
inlinevoid objc_object::clearDeallocating() { if (slowpath(!isa.nonpointer)) { // Slow path for raw pointer isa. // 没有使用优化的 isa 指针,清除 SideTable 中的引用计数的数据 sidetable_clearDeallocating(); } elseif (slowpath(isa.weakly_referenced || isa.has_sidetable_rc)) { // Slow path for non-pointer isa with weak refs and/or side table data. // 有弱指针 或者 使用 SideTable 管理引用计数 clearDeallocating_slow(); }