Write in front: iOS underlying principle exploration is my usual development and learning in the accumulation of a section of advanced road. Record my continuous exploration of the journey, I hope to be helpful to all readers.Copy the code

The directory is as follows:

  1. IOS underlying principles of alloc exploration
  2. The underlying principles of iOS are explored
  3. The underlying principles of iOS explore the nature of objects & isa’s underlying implementation
  4. Isa-basic Principles of iOS (Part 1)
  5. Isa-basic Principles of iOS (Middle)
  6. Isa-class Basic Principles of iOS Exploration (2)
  7. IOS fundamentals explore the nature of Runtime Runtime & methods
  8. Objc_msgSend: Exploring the underlying principles of iOS
  9. Slow lookups in iOS Runtime
  10. A dynamic approach to iOS fundamentals
  11. The underlying principles of iOS explore the message forwarding process
  12. Dyld (part 1)
  13. IOS Basic Principles of application loading principle dyld (ii)
  14. IOS basic principles explore the loading of classes
  15. The underlying principles of iOS explore the loading of categories
  16. IOS underlying principles to explore the associated object
  17. IOS underlying principle of the wizard KVC exploration
  18. Exploring the underlying principles of iOS: KVO Principles | More challenges in August
  19. Exploring the underlying principles of iOS: Rewritten KVO | More challenges in August
  20. The underlying principles of iOS: Multi-threading | More challenges in August
  21. GCD functions and queues in iOS
  22. GCD principles of iOS (Part 1)
  23. IOS Low-level – What do you know about deadlocks?
  24. IOS Low-level – Singleton destruction is possible?
  25. IOS Low-level – Dispatch Source
  26. IOS bottom – a fence letter blocks the number
  27. IOS low-level – Be there or be Square semaphore
  28. IOS underlying GCD – In and out into a scheduling group
  29. Basic principles of iOS – Basic use of locks
  30. IOS underlying – @synchronized Flow analysis
  31. IOS low-level – The principle of lock exploration
  32. IOS Low-level – allows you to implement a read/write lock
  33. Implementation of Objective-C Block
  34. Implementation of Objective-C Block
  35. IOS bottom – Block, comprehensive resolution!
  36. IOS Basics – Startup Optimization (part 1)
  37. IOS Basics – Startup Optimization (2)
  38. Exploration of basic principles of iOS — Memory management of memory five areas
  39. Tagged Pointer Format Changes for memory management

Summary of the above column

  • Summary of iOS underlying principles of exploration

Sort out the details

  • Summary of iOS development details

preface

Following on from the previous article, we moved on to the OC Memory management series on the flow of weak reference tables.

Sidetable_retain begins today’s source code reading


id
objc_object: :sidetable_retain(bool locked){#ifSUPPORT_NONPOINTER_ISA ASSERT(! isa.nonpointer); #endif SideTable& table = SideTables()[this];
    
    if(! locked) table.lock(); size_t& refcntStorage = table.refcnts[this];
    if (! (refcntStorage & SIDE_TABLE_RC_PINNED)) {
        refcntStorage += SIDE_TABLE_RC_ONE;
    }
    table.unlock();

    return (id)this;
}
Copy the code

SideTable

struct SideTable {
    spinlock_t slock;
    // Reference count table
    RefcountMap refcnts; 
    // weak reference table (__weak)
    weak_table_t weak_table;

    SideTable() {
        memset(&weak_table, 0, sizeof(weak_table));
    }

    ~SideTable() {
        _objc_fatal("Do not delete SideTable.");
    }

    void lock() { slock.lock(); }
    void unlock() { slock.unlock(); }
    void forceReset() { slock.forceReset(); }

    // Address-ordered lock discipline for a pair of side tables.

    template<HaveOld, HaveNew>
    static void lockTwo(SideTable *lock1, SideTable *lock2);
    template<HaveOld, HaveNew>
    static void unlockTwo(SideTable *lock1, SideTable *lock2);
};
Copy the code

The system maintains multiple sideTables (if there is only one table, all the objects will be very expensive to use (query, lock)), and the multiple tables will store the objects separately, with the use of the freed objects can be optimized for table storage. The routine operation of space for time.

A weak reference table

Start with a test print that doesn’t look very scientific:

Let’s do the assembly

It goes to objc_initWeak, why is that? Because in LLVM symbols are bound. Just like before super, a specific identifier will be found.

objc_initWeak

/** * initializes a new weak pointer to an object location. * It will be used for The following code: * * (The nil case) * __weak ID weakPtr; * (The non-nil case) * NSObject *o = ... ; * __weak id weakPtr = o; * * This function is not thread-safe for concurrency * modification of weak variables. (Concurrent weak cleanup 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

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;// Get locks for old and new values.
    // Order by lock address to prevent lock order problems.
    // If our old values below change, please try again.
 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;
    }

    // Prevent deadlocks between weak reference mechanisms
    // And class initialization mechanism to ensure that there is no
    // Weak reference objects have uninitialized ISA
    if (haveNew  &&  newObj) {
        Class cls = newObj->getIsa();
        if(cls ! = 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; goto retry; }}// Remove old values, if any.
    if (haveOld) {
        weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
    }

    // If there is a new value, the new value is assigned
    if (haveNew) {
        newObj = (objc_object *)
            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;
    }
    else {
        // No new value. The storage is not changed.
    }
    
    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
haveOld = nil; oldTable = nil;
haveNew = YES; newTable = newTable = &SideTables()[newObj];
Copy the code

haveNew = YES; 
newObj = (objc_object *)weak_register_no_lock(&newTable->weak_table, (id)newObj, location, crashIfDeallocating ? CrashIfDeallocating : ReturnNilIfDeallocating);
Copy the code

Finally return out;

Weak Process Summary

  • 1: First of all, we know that there is a really cool guy named sideTable
  • 2: Obtain the weakTable weak reference table of sideTable
  • 3: Create a Weak_entry_t
  • 4: Add referent to weak_entry_t array inline_Referrers
  • 5: Expand the weak_table
  • 6: Add new_entry to weak_table

Going back to __weak why is 2 printed?

Let’s break it up and assemble it:

As you can see, objc_loadWeakRetained is called

objc_loadWeakRetained

Here we do an obj->rootTryRetain() operation, which follows the retain process we explored earlier;

Why would Apple do that?

Then look at:

That is, weakObjc and objC are independent of each other. After the middle code block, weakObjc points to nil. There is no relationship between objC and objC; WeakObjc simply loads weak references in the table.

It will crash because you can’t find the weak reference table. .