1, Object Runtime — Weak 2, iOS low-level parsing Weak 3, Objective-C Runtime mechanism (7)

This article will discuss the use of weak from the following aspects:

  • Add weak references (an __weak modified variable, what’s going on underneath)
  • Weak Specifies the data structure related to
  • Delete weak references (dealloc is executed when the reference count of an object is 0, and weak references of the object are removed)

Add a weak reference

Let’s start with an example:

    NSObject *obj = [[NSObject alloc] init];
    __weak NSObject *weakObj = obj;
Copy the code

__weak NSObject *weakObj = obj, this code is actually called the following underlying code, specific can see the Runtime source, nsobject.mm file

StoreWeak <DontHaveOld, DoHaveNew, DoCrashIfDeallocating> (location, (objc_object*)newObj) 3. Weak_register_no_lock () is called internally by weak_register_no_lock(), which handles weak reference logic. The storeWeak function is called internally; 2. Inside the Weak_register_no_lock () function, weak_register_no_lock() function will get the SideTable where the weak reference object is located, SideTable has a Weak_table_t structure, weak_table contains a Weak_entries array, Weak_entry_t structure, each object corresponds to an entry, entry manages the weak reference relation array of the objectCopy the code

Take a look at the data structure before looking at the actual code:

2. Relevant data structures

1, StripedMap internal installed SideTable 2, SideTable encapsulates weak_table_t 3, weak_table_t global weak table 4, weak_entry_t object corresponding weak reference 5. Weak_referrer_t Weak referenceCopy the code

As shown below:

Take a look at the data structure:

// StripedMap<T> is a map of void* -> T, sized appropriately // for cache-friendly lock striping. // For example, this may be used as StripedMap<spinlock_t> // or as StripedMap<SomeStruct> where SomeStruct stores a spin lock. // <typename T> class StripedMap {#if TARGET_OS_IPHONE &&! TARGET_OS_SIMULATOR enum { StripeCount = 8 }; #else enum { StripeCount = 64 }; #endif struct PaddedT { T value alignas(CacheLineSize); }; PaddedT array[StripeCount]; // Hash value generating function, Static unsigned int indexForPointer(const void *p) {uintptr_t addr = reinterpret_cast<uintptr_t>(p); return ((addr >> 4) ^ (addr >> 9)) % StripeCount; }... } 2, struct SideTable {spinlock_t slock; // spinlock RefcountMap refcnts; Weak_table_t Weak_table; // Object - reference count Hash table obtains reference count based on object pointer. Weak_entries SideTable() {// Initialize memset(& Weak_table, 0, sizeof(weak_table)); // Weak_entries SideTable() {// Initialize memset(& Weak_table, 0, sizeof(weak_table)); } ~SideTable() {// destructor _objc_fatal("Do not delete SideTable. }... } 3, Weak_table_t /** * The global weak references table. Stores object ids as keys, * and weak_entry_t structs as their values. */ struct weak_table_t {weak_entry_t *weak_entries; // entry array size_t num_entries; // Uintptr_t mask; Uintptr_t max_hash_displacement; }; 4, Weak_entry_t struct Weak_entry_t {DisguisedPtr<objc_object> Referent; When the number of weak references is greater than 4, use the mutable array referrers. When the number of weak references is less than or equal to 4, use the mutable array referrers. Inline_referrers union {struct {weak_referrer_t *referrers; // Uintptr_out_of_line_ness: 2; // Uintptr_t num_refs: PTR_MINUS_2; // Uintptr_t mask; // Use uintptr_t max_hash_displacement to help calculate index where weak references are located; };};}; Struct {// out_of_line_ness field is low bits of inline_referrers[1] // fixed length array Weak_referrer_t inline_referrers[WEAK_INLINE_COUNT]; }; }; bool out_of_line() { return (out_of_line_ness == REFERRERS_OUT_OF_LINE); } // = operator overload weak_entry_t& operator=(const weak_entry_t& other) {memcpy(this, &other, sizeof(other)); // copy this to other memory address return *this; Weak_entry_t (objc_object *newReferent, objc_object **newReferrer) : Inline_referrers [0] = newReferrer; inline_referrers[0] = newReferrer; for (int i = 1; i < WEAK_INLINE_COUNT; i++) { inline_referrers[i] = nil; }}};Copy the code

Add weak reference concrete implementation: Runtime source code

// static template <HaveOld HaveOld, HaveNew HaveNew, enum CrashIfDeallocating crashIfDeallocating> static id storeWeak(id *location, objc_object *newObj) { ASSERT(haveOld || haveNew); if (! haveNew) ASSERT(newObj == nil); Class previouslyInitializedClass = nil; // mark the uninitialized class id oldObj; // old value SideTable *oldTable; // old SideTable SideTable *newTable; // Acquire locks for old and new values. Order by lock address to prevent lock ordering problems. // Try if the old value changes Puppy puppy us. // If (haveOld) {// oldObj = *location; // oldObj = *location; oldTable = &SideTables()[oldObj]; } else { oldTable = nil; } if (haveNew) {// Get new SideTable from sidebales if there is a new value otherwise assign nil newTable = &SideTables()[newObj]; } else { newTable = nil; SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable); SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable); // Retry if (haveOld && *location! = oldObj) { SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable); goto 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. // By making sure that weak reference objects have -+initialized isa Pointers, If (haveNew && newObj) {Class CLS = newObj->getIsa(); Isa is the first element of the object. All isa addresses are pointer addresses of the class. = previouslyInitializedClass && ! ((objc_class *)cls)->isInitialized()) { SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable); class_initialize(cls, (id)newObj); // 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; // mark the class as initialized and retry goto retry; }} // Clean up old value, if any. If (haveOld) { Weak_unregister_no_lock (&oldTable-> Weak_table, oldObj, location); weak_unregister_no_lock(&oldTable-> Weak_table, oldObj, location); } // Assign new value, If (haveNew) {newObj = (objc_object *) Weak_register_no_lock (&newTable->weak_table,) weak_register_no_lock(&newTable->weak_table, (id)newObj, location, crashIfDeallocating ? CrashIfDeallocating : ReturnNilIfDeallocating); // weak_register_no_lock returns nil if weak store should be rejected // Set is-weakly-referenced bit in refcount table.  if (! newObj->isTaggedPointerOrNil()) { newObj->setWeaklyReferenced_nolock(); } // Do not set *location anywhere else. That would introduce a race. *location = (id)newObj; SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable); // This must be called without the locks held, as it can invoke // arbitrary code. In particular, even if _setWeaklyReferenced // is not implemented, resolveInstanceMethod: may be, and may // call back into the weak reference machinery. callSetWeaklyReferenced((id)newObj); Return (id)newObj; }Copy the code

Delete weak references

When an object is released, the system calls the dealloc method, which removes the weak reference of the current object as follows:

1, objc_destructInstance() 2, _objc_rootDealloc(self) 3, obj->rootDealloc() 4, objc_destructInstance() 8, Weak_clear_NO_lock (&table.weak_table, (ID)this)Copy the code

Four,

1. Where are weak references stored?

SideTable -> Weak_table_t -> Weak_entry_t -> Referrers array in the corresponding Weak_entry_t of the object

2. Is the process for setting an __weak attribute the same as for setting an __weak variable

It is the same, but the timing of the call is different. Weak refers to the property. The objc_storeWeak() function is called when the class is loading a member variable

3. When are weakly referenced objects set to nil?

When the system executes dealloc, the system processes the dealloc internally

Knowledge points involved

Hash table 2. TaggedPointer 3. Dealloc 4