iOS 從源碼深入探究 weak 的實現

404355009 7年前發布 | 12K 次閱讀 iOS開發 移動開發 Objective-C

__weak

id __week obj1 = obj;

編譯器的模擬代碼

id obj1;
    obj1 = 0;
    objc_storeWeak(&obj1, obj);
    objc_storeWeak(&obj1, 0);

objc_storeWeak

函數把第二參數的賦值對象的地址作為鍵值,將第一參數的附有__weak修飾的變量的地址注冊到weak表中。

如果第二參數為0,則把變量的地址從weak表中刪除。

initWeak的實現

id objc_initWeak(id *object, id value)
{
    *object = 0;
    return objc_storeWeak(object, value);
}

storeWeak是Objective-C的開源部分

讓我們來看看storeWeak到底是怎么實現的

objc_storeWeak

storeWeak的源碼

官方英文注釋挺全的,可以直接理解~

// Clean up old value, if any.
    if (HaveOld) {
        weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
    }

// Assign new value, if any.
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.
    if (newObj  &&  !newObj->isTaggedPointer()) {
        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.
}</code></pre> 

來看看storeWeak的實現

獲取oldObj/newObj

if (HaveOld) {
        oldObj = *location;
        oldTable = &SideTables()[oldObj];
    } else {
        oldTable = nil;
    }
    if (HaveNew) {
        newTable = &SideTables()[newObj];
    } else {
        newTable = nil;
    }

首先是根據weak指針找到其指向的老的對象:

oldObj = *location;

然后獲取到與新舊對象相關的SideTable對象:

oldTable = &SideTables()[oldObj];
newTable = &SideTables()[newObj];

&SideTables()[oldObj]這是什么鬼??

其時是 實現了一個類 StripedMap 重載了[]操作符

(c++: 哪里都能看到我 233)

public:
    T& operator[] (const void *p) { 
        return array[indexForPointer(p)].value; 
    }

下面要做的就是在老對象的weak表中移除指向信息,而在新對象的weak表中建立關聯信息:

if (HaveOld) {
    weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
}
if (HaveNew) {
    newObj = weak_register_no_lock(&newTable->weak_table, newObj,location);
    // weak_register_no_lock returns NULL if weak store should be rejected
}

接下來讓弱引用指針指向新的對象:

*location = newObj;

最后會返回這個新對象:

return newObj;

以上我們能發現weak的管理實際上跟weak_table有這千絲萬縷的聯系,接下來就對weak_table進行分析!

weakTable

(關先上源碼還是先總結...我思考了很久...。。。。)

  • weak表是一個弱引用表,實現為一個weak_table_t結構體,存儲了所有對象相關的的所有的弱引用信息
  • 其中weak_entry_t是存儲在弱引用表中的一個內部結構體,它負責維護和存儲指向一個對象的所有弱引用hash表。
  • weak_entry_t中的referrers 存儲了指向weak對象的所有變量

來張圖直觀感受一下:

下面開始對這些結構體進行分析:

  • SideTable是一個用C++實現的類,它的具體定義在NSObject.mm中
    class SideTable {
    private:
      static uint8_t table_buf[SIDE_TABLE_STRIPE * SIDE_TABLE_SIZE];
    public:
      RefcountMap refcnts;//引用計數表
      weak_table_t weak_table;//弱引用表
      ......
    }

weak表的結構定義:

/**
 * 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;
    size_t    num_entries;
    uintptr_t mask;
    uintptr_t max_hash_displacement;
};

根據注釋我們可以得到這是一張全局的存儲object的id 和 keys的表.

weak_entry_t 作為他們的值.

來看weak_entry_t的結構體

struct weak_entry_t {
    DisguisedPtr<objc_object> referent;
    union {
        struct {
            weak_referrer_t *referrers;
            uintptr_t        out_of_line : 1;
            uintptr_t        num_refs : PTR_MINUS_1;
            uintptr_t        mask;
            uintptr_t        max_hash_displacement;
        };
        struct {
            // out_of_line=0 is LSB of one of these (don't care which)
            weak_referrer_t  inline_referrers[WEAK_INLINE_COUNT];
        };
    };
};

referrers: 是指向weak對象的所有變量

referent: 是內存上的weak對象

現在我們可以得出什么結論了呢

  1. OC中 弱引用變量的管理是利用 weak表(Hash表)來管理的
  2. weak表中的weak_entries負責管理指向weak對象的變量

weak對象的釋放

釋放對象時,廢棄誰都不持有的對象的同時,程序的動作是怎么樣的呢?

  • objc_release
  • 因為引用計數為0所以執行dealloc
  • objc rootDealloc
  • objc dispose
  • objc destructInstance
  • objc clear dealloctaing

對象被廢棄時最后調用的objc_clear_deallocating函數的動作如下:

(1) 從weak表中獲取廢棄對象的地址為鍵值的記錄。

(2) 將包含在記錄中的所有附有__weak修飾符變量的地址,賦值為nil

(3) 從weak表中刪除該記錄。

(4) 從引用計數表中刪除廢棄對象的地址為鍵值的記錄。

由此可知,如果大量使用附有weak修飾符的變量,則會消耗相應的CPU資源。良策是只在需要避免循環引用時使用weak修飾符。

立即釋放對象

{
    id __weak obj = [[NSObject alloc] init];
}

因為該源碼將自己生成并持有的對象賦值給附有__weak修飾符的變量中,所以自己不能持有該對象,這是會被釋放并且廢棄。

使用附有__weak修飾符的變量,即是使用注冊到autoreleasepool中的對象

{
    id __weak obj1 = obj;
    NSLog(@"%@",obj1);
}

編譯器模擬的代碼

id obj1;
    objc_initWeak(&obj,obj);
    id tmp = objc_loadWeakRetained(&obj);
    objc_autorelease(tmp);
    NSLog(@"%@", tmp);
    objc_destoryWeak(&obj1);

注意這兩行代碼

id tmp = objc_loadWeakRetained(&obj);
    objc_autorelease(tmp);

與賦值時相比,在使用附有__weak修飾符變量的情況下,增加了對objc_loadWeakRetained函數和objc_autorelease函數的調用。

(1) objc_loadWeakRetained 函數取出附有__weak修飾符變量所引用的對象并retain

(2) objc_autorelease 函數將對象注冊到autoreleasepool中。

注意:

每次使用

weak修飾的變量,會使變量所引用的對象注冊到autoreleasepool中。

如果要避免這種情況可以將附有

weak修飾符的變量賦值給附有__strong修飾符的變量后再次使用。

id __weak o = obj;
id tmp = o;

allowWeakReference/retainWeakReference

當allowsWeakReference/retainWeakReference實例方法(沒有寫入NSObject接口說明文檔中)返回NO的情況。

- (BOOL)allowsWeakReference;
- (BOOL)retainWeakReference;

在賦值給__weak修飾符的變量時,如果allowsWeakReference方法返回NO,程序將異常終止。

對象retain時,如果retainWeakReference方法返回NO, 該變量將使用nil

具體源碼分析

以上關于weak的

  • weak_register_no_lock
  • weak_unregister_no_lock
  • 。。。
    很多具體實現都沒有講...
    我把自己看的代碼加上注釋貼出來了...感興趣的可以看一下具體的實現...感受源碼實現的魅力

weak_unregister_no_lock

怎么理解呢objc_object **referrer?

objc_object *referent = (objc_object *)referent_id;
objc_object **referrer = (objc_object **)referrer_id;

我們要結合remove_referrer這個函數來理解

remove_referrer

for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
            if (entry->inline_referrers[i] == old_referrer) {
                entry->inline_referrers[i] = nil;
                return;
            }
        }

所以我們要拿到referrer 根據這個值來和entry鏈表中的指針進行比較,如果發現,就nil

(指針搞得我都暈了...佩服c/c++系統工程師)

(閱讀源碼真的是一件有意思的是哈哈)

#include "objc-private.h"

#include "objc-weak.h"

#include <stdint.h>
#include <stdbool.h>
#include <sys/types.h>
#include <libkern/OSAtomic.h>

#define TABLE_SIZE(entry) (entry->mask ? entry->mask + 1 : 0)

static void append_referrer(weak_entry_t *entry, objc_object **new_referrer);

BREAKPOINT_FUNCTION(
    void objc_weak_error(void)
);

/** 
 * Unique hash function for object pointers only.
 * 
 * @param key The object pointer
 * 
 * @return Size unrestricted hash of pointer.
 */
static inline uintptr_t hash_pointer(objc_object *key) {
    return ptr_hash((uintptr_t)key);
}

/** 
 * Unique hash function for weak object pointers only.
 * 
 * @param key The weak object pointer. 
 * 
 * @return Size unrestricted hash of pointer.
 */
static inline uintptr_t w_hash_pointer(objc_object **key) {
    return ptr_hash((uintptr_t)key);
}

/** 
 * Grow the entry's hash table of referrers. Rehashes each
 * of the referrers.
 * 
 * @param entry Weak pointer hash set for a particular object.
 */
__attribute__((noinline, used))
static void grow_refs_and_insert(weak_entry_t *entry, 
                                 objc_object **new_referrer)
{
    assert(entry->out_of_line);

    size_t old_size = TABLE_SIZE(entry);
    size_t new_size = old_size ? old_size * 2 : 8;

    size_t num_refs = entry->num_refs;
    weak_referrer_t *old_refs = entry->referrers;
    entry->mask = new_size - 1;

    entry->referrers = (weak_referrer_t *)
        calloc(TABLE_SIZE(entry), sizeof(weak_referrer_t));
    entry->num_refs = 0;
    entry->max_hash_displacement = 0;

    for (size_t i = 0; i < old_size && num_refs > 0; i++) {
        if (old_refs[i] != nil) {
            append_referrer(entry, old_refs[i]);
            num_refs--;
        }
    }
    // Insert
    append_referrer(entry, new_referrer);
    if (old_refs) free(old_refs);
}

/** 
 * 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.
 */
static void append_referrer(weak_entry_t *entry, objc_object **new_referrer)
{
    // if is Array implementation
    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 = 1;
        entry->mask = WEAK_INLINE_COUNT-1;
        entry->max_hash_displacement = 0;
    }

    assert(entry->out_of_line);

    if (entry->num_refs >= TABLE_SIZE(entry) * 3/4) {
        return grow_refs_and_insert(entry, new_referrer);
    }

    //find a place to insert ref
    //weak_entry_remove() may bzero() some place
    size_t index = w_hash_pointer(new_referrer) & (entry->mask);
    size_t hash_displacement = 0;
    while (entry->referrers[index] != NULL) {
        index = (index+1) & entry->mask;
        hash_displacement++;
    }
    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. 
 */
static void 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 index = w_hash_pointer(old_referrer) & (entry->mask);
    size_t hash_displacement = 0;
    while (entry->referrers[index] != old_referrer) {
        index = (index+1) & entry->mask;
        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--;
}

/** 
 * Add new_entry to the object's table of weak references.
 * Does not check whether the referent is already in the table.
 */
static void weak_entry_insert(weak_table_t *weak_table, weak_entry_t *new_entry)
{
    weak_entry_t *weak_entries = weak_table->weak_entries;
    assert(weak_entries != nil);

    //mask may keep entry in array
    size_t index = hash_pointer(new_entry->referent) & (weak_table->mask);
    size_t hash_displacement = 0;

    //hash index 處理
    while (weak_entries[index].referent != nil) {
        index = (index+1) & weak_table->mask;
        hash_displacement++;
    }


    weak_entries[index] = *new_entry;
    weak_table->num_entries++;

    //update max_hash_displacement
    if (hash_displacement > weak_table->max_hash_displacement) {
        weak_table->max_hash_displacement = hash_displacement;
    }
}


static void weak_resize(weak_table_t *weak_table, size_t new_size)
{
    size_t old_size = TABLE_SIZE(weak_table);

    weak_entry_t *old_entries = weak_table->weak_entries;
    weak_entry_t *new_entries = (weak_entry_t *)
        calloc(new_size, sizeof(weak_entry_t));

    weak_table->mask = new_size - 1;
    //new
    weak_table->weak_entries = new_entries;
    weak_table->max_hash_displacement = 0;
    weak_table->num_entries = 0;  // restored by weak_entry_insert below

    //use pointer
    if (old_entries) {
        weak_entry_t *entry;
        weak_entry_t *end = old_entries + old_size;
        for (entry = old_entries; entry < end; entry++) {
            if (entry->referent) {
                weak_entry_insert(weak_table, entry);
            }
        }
        free(old_entries);
    }
}

// Grow the given zone's table of weak references if it is full.
static void weak_grow_maybe(weak_table_t *weak_table)
{
    size_t old_size = TABLE_SIZE(weak_table);

    // Grow if at least 3/4 full.
    if (weak_table->num_entries >= old_size * 3 / 4) {
        weak_resize(weak_table, old_size ? old_size*2 : 64);
    }
}

// Shrink the table if it is mostly empty.
static void weak_compact_maybe(weak_table_t *weak_table)
{
    size_t old_size = TABLE_SIZE(weak_table);

    // Shrink if larger than 1024 buckets and at most 1/16 full.
    if (old_size >= 1024  && old_size / 16 >= weak_table->num_entries) {
        weak_resize(weak_table, old_size / 8);
        // leaves new table no more than 1/2 full
    }
}


/**
 * Remove entry from the zone's table of weak references.
 */
static void weak_entry_remove(weak_table_t *weak_table, weak_entry_t *entry)
{
    // remove entry
    if (entry->out_of_line) free(entry->referrers);

    //bzero()函數在由s指向的區域中放置n個0。
    bzero(entry, sizeof(*entry));

    weak_table->num_entries--;

    //maybe resize weak_table
    weak_compact_maybe(weak_table);
}


/** 
 * 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);


    weak_entry_t *weak_entries = weak_table->weak_entries;

    if (!weak_entries) return nil;

    size_t index = hash_pointer(referent) & weak_table->mask;
    size_t hash_displacement = 0;
    while (weak_table->weak_entries[index].referent != referent) {
        index = (index+1) & weak_table->mask;
        hash_displacement++;
        if (hash_displacement > weak_table->max_hash_displacement) {
            return nil;
        }
    }

    return &weak_table->weak_entries[index];
}

/** 
 * Unregister an already-registered weak reference.
 * This is used when referrer's storage is about to go away, but referent
 * isn't dead yet. (Otherwise, zeroing referrer later would be a
 * bad memory access.)
 * Does nothing if referent/referrer is not a currently active weak reference.
 * Does not zero referrer.
 * 
 * FIXME currently requires old referent value to be passed in (lame)
 * FIXME unregistration should be automatic if referrer is collected
 * 
 * @param weak_table The global weak table.
 * @param referent The object.
 * @param referrer The weak reference.
 */
void
weak_unregister_no_lock(weak_table_t *weak_table, id referent_id, 
                        id *referrer_id)
{
    objc_object *referent = (objc_object *)referent_id;
    objc_object **referrer = (objc_object **)referrer_id;

    weak_entry_t *entry;

    if (!referent) return;


    if ((entry = weak_entry_for_referent(weak_table, referent))) {
        remove_referrer(entry, referrer);
        bool empty = true;
        //after unregister the entry's referrers is empty?
        // Hash implementation
        if (entry->out_of_line  &&  entry->num_refs != 0) {
            empty = false;
        }
        // Array implementation
        else {
            for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
                if (entry->inline_referrers[i]) {
                    empty = false; 
                    break;
                }
            }
        }
        // if entry.references empty
        if (empty) {
            weak_entry_remove(weak_table, entry);
        }
    }

    // Do not set *referrer = nil. objc_storeWeak() requires that the 
    // value not change.
}

/** 
 * 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)
{
    //object
    objc_object *referent = (objc_object *)referent_id;

    //The Point which point the object
    objc_object **referrer = (objc_object **)referrer_id;

    if (!referent  ||  referent->isTaggedPointer()) return referent_id;

    // ensure that the referenced object is viable
    // judge is Allows Weak Reference
    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) {
            return nil;
        }
        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 {
            return nil;
        }
    }

    // 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;
        new_entry.referent = referent;
        new_entry.out_of_line = 0;
        new_entry.inline_referrers[0] = referrer;
        for (size_t i = 1; i < WEAK_INLINE_COUNT; i++) {
            new_entry.inline_referrers[i] = nil;
        }

        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;
}


#if DEBUG
bool
weak_is_registered_no_lock(weak_table_t *weak_table, id referent_id) 
{
    return weak_entry_for_referent(weak_table, (objc_object *)referent_id);
}
#endif


/** 
 * 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) 
{

    //referent objc
    objc_object *referent = (objc_object *)referent_id;

    //referent objc entry(which save many referents)
    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;

    if (entry->out_of_line) {
        referrers = entry->referrers;
        count = TABLE_SIZE(entry);
    } 
    else {
        referrers = entry->inline_referrers;
        count = WEAK_INLINE_COUNT;
    }

    //entry->referrers all nil
    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();
            }
        }
    }

    weak_entry_remove(weak_table, entry);
}


/** 
 * This function gets called when the value of a weak pointer is being 
 * used in an expression. Called by objc_loadWeakRetained() which is
 * ultimately called by objc_loadWeak(). The objective is to assert that
 * there is in fact a weak pointer(s) entry for this particular object being
 * stored in the weak-table, and to retain that object so it is not deallocated
 * during the weak pointer's usage.
 * 
 * @param weak_table 
 * @param referrer The weak pointer address. 
 */
/*
  Once upon a time we eagerly cleared *referrer if we saw the referent 
  was deallocating. This confuses code like NSPointerFunctions which 
  tries to pre-flight the raw storage and assumes if the storage is 
  zero then the weak system is done interfering. That is false: the 
  weak system is still going to check and clear the storage later. 
  This can cause objc_weak_error complaints and crashes.
  So we now don't touch the storage until deallocation completes.
*/
id 
weak_read_no_lock(weak_table_t *weak_table, id *referrer_id) 
{
    objc_object **referrer = (objc_object **)referrer_id;
    objc_object *referent = *referrer;

    //Detection Tagged Pointer
    if (referent->isTaggedPointer()) return (id)referent;

    weak_entry_t *entry;
    // referent == nil or entry == nil
    if (referent == nil  ||  
        !(entry = weak_entry_for_referent(weak_table, referent))) 
    {
        return nil;
    }
    //Custom RR denotes a custom retain-release implementation
    //
    if (! referent->ISA()->hasCustomRR()) {
        //???question
        if (! referent->rootTryRetain()) {
            return nil;
        }
    }
    //has isa
    else {
        BOOL (*tryRetain)(objc_object *, SEL) = (BOOL(*)(objc_object *, SEL))
            object_getMethodImplementation((id)referent, 
                                           SEL_retainWeakReference);
        //IMP != _objc_magForward
        if ((IMP)tryRetain == _objc_msgForward) {
            return nil;
        }
        //IMP != nil
        if (! (*tryRetain)(referent, SEL_retainWeakReference)) {
            return nil;
        }
    }

    return (id)referent;
}

 

來自:https://juejin.im/post/58ffe5fb5c497d0058158fee

 

 本文由用戶 404355009 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!