1. Weak

In iOS development, the weak keyword is often used to solve the problem that memory leaks cannot be released due to strong references between objects. The weak keyword does not increase the reference count by 1 and is set to nil when the reference objects are released, which also avoids the crash caused by bad memory access by wild Pointers.

Extension: Why does the modifier agent use weak instead of assign?

Assign can be used to modify basic data types as well as objects of OC. However, if you use the assign object type to point to a strong pointer, after the pointer is freed, it still points to that block of memory. You must set it to nil manually otherwise wild Pointers will be generated. Causes an EXC_BAD_ACCESS error, calling the memory space that has been freed. Weak can only be used to modify OC objects and is safer than assign. If the object to which it points disappears, it is automatically set to nil. This does not result in wild Pointers.

2. Weak workflow

  1. Find our Weak_table by SideTable
  2. Weak_table finds or creates weak_entry_t according to the referent
  3. Append_referrer (entry, referrer) then adds the new weak reference object to the entry
  4. Finally, weak_entry_INSERT adds entry to weak_table

3. Weak low-level exploration

3.1 objc_initweak

Run xcode to enable the debug mode, and you can see the assembly code as follows

0x100003c14 <+88>:  bl     0x100003cc8               ; symbol stub for: objc_initWeak
0x100003c1c <+96>:  bl     0x100003cbc               ; symbol stub for: objc_destroyWeak
Copy the code

Objc_initWeak is an entry function for weak, and then storeWeak

/** * Initialize a fresh weak pointer to some object location. * It would be used for code like: * * (The nil case) * __weak id weakPtr; * (The non-nil case) * NSObject *o = ... ; * __weak id weakPtr = o; * * This function IS NOT thread-safe with respect to concurrent * modifications to the weak variable. (Concurrent weak clear is safe.) * * @param location Address of __weak ptr. * @param newObj Object ptr. */
id
objc_initWeak(id *location, id newObj)
{
    if(! newObj) { *location = nil;return nil;
    }
    return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>

        (location, (objc_object*)newObj);
}
Copy the code

3.2 storeWeak

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;

    /* omit the intermediate code */
    if (haveOld) {// Old values exist. Use hash to find and erase old values
        weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
    }
    // Assign new value, if any.
    if (haveNew) {// There is a new value, insert the new value using the Weak_register_no_lock method
        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.
        if(newObj && ! newObj->isTaggedPointer()) {
            newObj->setWeaklyReferenced_nolock(a); }// Do not set *location anywhere else. That would introduce a race.
        *location = (id)newObj;
    }
    else {
        // No new value. The storage is not changed.
    }

    SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
    return (id)newObj;

}
Copy the code

3.3 weak_register_no_lock

Weak_table determines the destruct first

/** * Registers a new (object, weak pointer) pair. Creates a new weak * object entry if it does not exist. * * @param weak_table The global weak table.  * @param referent The object pointed to by the weak reference. * @param referrer The weak pointer address. */ 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; Now remember it and where it is being stored Weak_entry_t *entry; // Now remember it and where it is being stored 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); } // Do not set *referrer. objc_storeWeak() requires that the // value not change. return referent_id; }Copy the code

3.3 weak_entry

Append_referrer adds the object new_referrer to weak_entry’s entry-> Referrers array using the hash function in the weak reference table (also a hash table)

3.4 dealloc

How to set weak application object to nil by sidetable_clearDeallocating

/** * 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;
    // Key code, through the previous hash table, find the weak application table, through the referent in the weak_table to find the corresponding entity, and then traverse delete
    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;
    
    // delete the referent as the same as the referrer
    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(a); }}}weak_entry_remove(weak_table, entry);
}
Copy the code