How can the Objective-C runtime know whether a wea

2019-07-20 02:52发布

问题:

With the advent of ARC, some new functions were made available to allow developers to play around with weakly referenced objects. id objc_loadWeak(id *location) is one of them. This function receives one parameter correspoding to a location in memory where a weak object was stored and returns this object if it is still alive or nil if it were deallocated.

It seems that when an object obj is stored as weak in a location location with id objc_storeWeak(id *location, id obj), obj is put in a "weak map", with location as a key. However, in order to retrieve obj, objc_loadWeak can not only use location as a key and return the value, which corresponds to obj. It must also check whether obj is still alive to return nil if it is not anymore.

However, objc_loadWeak can not try to read the object's retain count, because the object may have been deallocated. Moreover, although the weak map, objc_storeWeak, objc_loadWeak and the NSObject class are implemented in the same file (NSObject.mm), NSObject's dealloc method doesn't signals to the weak map that the object that is being deallocated is going away.

So, how does the Objective-C runtime figure out whether a weak object is still alive?

回答1:

NSObject's dealloc method doesn't signals to the weak map that the object that is being deallocated is going away.

It does.

- [NSObject dealloc]

calls

_objc_rootDealloc(self);

which in turn invokes

object_dispose()

which in turn calls

objc_destructInstance()

which finally calls

objc_clear_deallocating()

This last function looks like this:

    void 
objc_clear_deallocating(id obj) 
{
    assert(obj);
    assert(!UseGC);

    SideTable *table = SideTable::tableForPointer(obj); /* *** THIS LINE *** */

    // clear any weak table items
    // clear extra retain count and deallocating bit
    // (fixme warn or abort if extra retain count == 0 ?)
    OSSpinLockLock(&table->slock);
    if (seen_weak_refs) {
    arr_clear_deallocating(&table->weak_table, obj); /* *** THIS LINE *** */
    }
    table->refcnts.erase(DISGUISE(obj)); /* *** THIS LINE *** */
    OSSpinLockUnlock(&table->slock);
}

The three highlighted lines do the magic. SideTable is a C++ class implemented in NSObject.mm of which the refcnts member variable does exactly what it sounds like it does: it holds reference counts.