iOS中Weak的底層實(shí)現(xiàn)

本文主要是解析Weak這種特性的底層實(shí)現(xiàn)。

weak 此特質(zhì)表明該屬性定義了一種“非擁有關(guān)系” (nonowning relationship)蹋辅。為這種屬性設(shè)置新值時(shí)送丰,設(shè)置方法既不保留新值怒详,也不釋放舊值。此特質(zhì)同 assign 類似烙懦, 然而在屬性所指的對(duì)象遭到摧毀時(shí)驱入,屬性值也會(huì)清空(nil out)。

以下的源碼來自于是Runtime最新版本objc4-723.tar.gz氯析。

先上一個(gè)個(gè)人整理的整體結(jié)構(gòu)圖亏较,好讓大家心中有數(shù)。

Screen Shot 2018-10-14 at 5.13.23 PM.png

結(jié)構(gòu)體

SideTables

SideTables用來管理所有對(duì)象的引用計(jì)數(shù)和weak指針掩缓,是一個(gè)全局的Hash表雪情。它使用對(duì)象的內(nèi)存地址當(dāng)它的key,SideTable結(jié)構(gòu)體為value你辣。

static StripedMap<SideTable>& SideTables() {
    return *reinterpret_cast<StripedMap<SideTable>*>(SideTableBuf);
}

這個(gè)有點(diǎn)難懂巡通,以下是weak 弱引用的實(shí)現(xiàn)方式的解釋:

在取出實(shí)例方法的實(shí)現(xiàn)中尘执,使用了 C++ 標(biāo)準(zhǔn)轉(zhuǎn)換運(yùn)算符 reinterpret_cast ,其表達(dá)方式為:

reinterpret_cast <new_type> (expression)

而 StripedMap 是一個(gè)模板類(Template Class)宴凉,通過傳入類(結(jié)構(gòu)體)參數(shù)誊锭,會(huì)動(dòng)態(tài)修改在該類中的一個(gè) array 成員存儲(chǔ)的元素類型,并且其中提供了一個(gè)針對(duì)于地址的 hash 算法弥锄,用作存儲(chǔ) key丧靡。可以說籽暇, StripedMap 提供了一套擁有將地址作為 key 的 hash table 解決方案温治,而該方案采用了模板類,是擁有泛型性的图仓。

SideTable

Side table 本質(zhì)就是用于保存額外信息的單獨(dú)內(nèi)存塊罐盔,并且它還是可選的但绕。也就是說救崔,對(duì)于那些無需保存額外信息的對(duì)象來說并沒有多余開銷。它主要用于管理對(duì)象的引用計(jì)數(shù)和 weak 表捏顺。


struct SideTable {
    spinlock_t slock;
    RefcountMap refcnts;
    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);
};


  • slock: 保證原子操作的自旋鎖六孵,避免計(jì)數(shù)錯(cuò)誤。這里蘋果為了性能考慮選擇自旋鎖幅骄。

自旋鎖是一種非阻塞鎖劫窒,也就是說,如果某線程需要獲取自旋鎖拆座,但該鎖已經(jīng)被其他線程占用時(shí)主巍,該線程不會(huì)被掛起,而是在不斷的消耗CPU的時(shí)間挪凑,不停的試圖獲取自旋鎖孕索。
互斥量是阻塞鎖,當(dāng)某線程無法獲取互斥量時(shí)躏碳,該線程會(huì)被直接掛起搞旭,該線程不再消耗CPU時(shí)間,當(dāng)其他線程釋放互斥量后菇绵,操作系統(tǒng)會(huì)激活那個(gè)被掛起的線程肄渗,讓其投入運(yùn)行。

自旋鎖比較適用于鎖使用者保持鎖時(shí)間比較短的情況咬最。正是由于自旋鎖使用者一般保持鎖時(shí)間非常短翎嫡,因此選擇自旋而不是睡眠是非常必要的,自旋鎖的效率遠(yuǎn)高于互斥鎖永乌。信號(hào)量和讀寫信號(hào)量適合于保持時(shí)間較長(zhǎng)的情況惑申,它們會(huì)導(dǎo)致調(diào)用者睡眠翁垂,因此只能在進(jìn)程上下文使用,而自旋鎖適合于保持時(shí)間非常短的情況硝桩,它可以在任何上下文使用沿猜。如果被保護(hù)的共享資源只在進(jìn)程上下文訪問,使用信號(hào)量保護(hù)該共享資源非常合適碗脊,如果對(duì)共享資源的訪問時(shí)間非常短啼肩,自旋鎖也可以。但是如果被保護(hù)的共享資源需要在中斷上下文訪問(包括底半部即中斷處理句柄和頂半部即軟中斷)衙伶,就必須使用自旋鎖祈坠。自旋鎖保持期間是搶占失效的,而信號(hào)量和讀寫信號(hào)量保持期間是可以被搶占的矢劲。自旋鎖只有在內(nèi)核可搶占或SMP(多處理器)的情況下才真正需要赦拘,在單CPU且不可搶占的內(nèi)核下,自旋鎖的所有操作都是空操作芬沉。另外格外注意一點(diǎn):自旋鎖不能遞歸使用躺同。

  • refcnts:引用計(jì)數(shù)的 hash 表,通過散列表的結(jié)構(gòu)存儲(chǔ)了對(duì)象持有者的地址以及引用計(jì)數(shù)
  • weak_table:weak 引用全局 hash 表

RefcountMap

有點(diǎn)復(fù)雜丸逸,想看詳細(xì)結(jié)構(gòu)的可以看這里llvm::DenseMapBase< DerivedT, KeyT, ValueT, KeyInfoT, BucketT > Class Template Reference

由于本篇文章主要不是講解引用計(jì)數(shù)原理的蹋艺,所以關(guān)于RefcountMap沒有多做研究,關(guān)于引用計(jì)數(shù)原理和RefcountMap推薦看這篇文章Objective-C 引用計(jì)數(shù)原理

weak_table_t

蘋果使用一個(gè)全局的 weak 表來保存所有的 weak 引用黄刚。使用對(duì)象地址為key捎谨,weak_entry_t結(jié)構(gòu)為value,weak_entry_t 中保存了所有指向該對(duì)象的 weak 指針憔维。
weak 表的作用是在對(duì)象執(zhí)行 dealloc 的時(shí)候?qū)⑺兄赶蛟搶?duì)象的 weak 指針的值設(shè)為 nil涛救,避免懸空指針。

/**
 * 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;
};
  • weak_entries:負(fù)責(zé)維護(hù)和存儲(chǔ)指向一個(gè)對(duì)象的所有弱引用的 hash 表
  • num_entries:負(fù)責(zé)記錄weak_table_t目前保存的entry的數(shù)目
  • mask:記錄weak_table_t的總?cè)萘?/li>
  • max_hash_displacement:記錄weak_table_t所有項(xiàng)的最大偏移量业扒,因?yàn)闀?huì)有hash碰撞的情況检吆,而weak_table_t采用了開放尋址來解決,所以某個(gè)entry實(shí)際存儲(chǔ)的位置并不一定是hash函數(shù)計(jì)算出來的位置

weak_entry_t

weak_entry_t 是存儲(chǔ)在弱引用表中的一個(gè)內(nèi)部結(jié)構(gòu)體凶赁,它負(fù)責(zé)維護(hù)和存儲(chǔ)指向一個(gè)對(duì)象的所有弱引用hash表咧栗。

struct weak_entry_t {
    DisguisedPtr<objc_object> referent;
    union {
        struct {
            weak_referrer_t *referrers;
            uintptr_t        out_of_line_ness : 2;
            uintptr_t        num_refs : PTR_MINUS_2;
            uintptr_t        mask;
            uintptr_t        max_hash_displacement;
        };
        struct {
            // out_of_line_ness field is low bits of inline_referrers[1]
            weak_referrer_t  inline_referrers[WEAK_INLINE_COUNT];
        };
    };

    bool out_of_line() {
        return (out_of_line_ness == REFERRERS_OUT_OF_LINE);
    }

    weak_entry_t& operator=(const weak_entry_t& other) {
        memcpy(this, &other, sizeof(other));
        return *this;
    }

    weak_entry_t(objc_object *newReferent, objc_object **newReferrer)
        : referent(newReferent)
    {
        inline_referrers[0] = newReferrer;
        for (int i = 1; i < WEAK_INLINE_COUNT; i++) {
            inline_referrers[i] = nil;
        }
    }
};

  • referent:被指對(duì)象的地址。前面循環(huán)遍歷查找的時(shí)候就是判斷目標(biāo)地址是否和他相等虱肄。
  • referrers:可變數(shù)組,里面保存著所有指向這個(gè)對(duì)象的弱引用的地址致板。當(dāng)這個(gè)對(duì)象被釋放的時(shí)候,referrers里的所有指針都會(huì)被設(shè)置成nil咏窿。
  • inline_referrers:只有4個(gè)元素的數(shù)組斟或,默認(rèn)情況下用它來存儲(chǔ)弱引用的指針。當(dāng)大于4個(gè)的時(shí)候使用referrers來存儲(chǔ)指針

weak_entry_t有一個(gè)巧妙的設(shè)計(jì)集嵌,即如果一個(gè)對(duì)象對(duì)應(yīng)的弱引用數(shù)目較少的話(<=WEAK_INLINE_COUNT萝挤,runtime把這個(gè)值設(shè)置為4)御毅,則其弱引用會(huì)被依次保存到一個(gè)inline數(shù)組里。這個(gè)inline數(shù)組的內(nèi)存會(huì)在weak_entry_t初始化的時(shí)候一并分配好怜珍,而不是需要用到的時(shí)候再去申請(qǐng)新的內(nèi)存空間端蛆,從而達(dá)到提到運(yùn)行效率的目的。此外酥泛,union中的兩個(gè)struct是共享同一塊內(nèi)存的今豆,如果不使用inline數(shù)組,而直接使用HashTable的方式來實(shí)現(xiàn)柔袁,那么num_refs呆躲,mask和max_hash_displacement這些變量都需要單獨(dú)的存儲(chǔ)空間,會(huì)使用更多的內(nèi)存捶索。綜上插掂,使用inline數(shù)組在節(jié)約一定內(nèi)存空間的同時(shí)還相對(duì)提高了運(yùn)行效率。

工作原理分析

objc_initWeak

在初始化一個(gè)weak對(duì)象時(shí)runtime會(huì)調(diào)用objc_initWeak方法腥例,這個(gè)方法的具體實(shí)現(xiàn)是在NSObject.mm文件中

/** 
 * 初始化一個(gè)指向某個(gè)對(duì)象地址的新指針辅甥。
 */
id
objc_initWeak(id *location, id newObj)
{
    if (!newObj) {
        *location = nil;
        return nil;
    }

    // 這里使用了Template(模板),有三個(gè)參數(shù)
    // DontHaveOld--沒有舊對(duì)象院崇,
    // DoHaveNew--有新對(duì)象肆氓,
    // DoCrashIfDeallocating-- 如果newObj已經(jīng)被釋放了就Crash提示
    return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
        (location, (objc_object*)newObj);
}

// Template 參數(shù)
enum HaveOld { DontHaveOld = false, DoHaveOld = true };
enum HaveNew { DontHaveNew = false, DoHaveNew = true };

// 更新weak變量.
// 當(dāng)設(shè)置HaveOld是true,即DoHaveOld底瓣,表示這個(gè)weak變量已經(jīng)有值,需要被清理蕉陋,這個(gè)值也有能是nil
// 當(dāng)設(shè)置HaveNew是true捐凭, 即DoHaveNew,表示有一個(gè)新值被賦值給weak變量凳鬓,這個(gè)值也有能是nil
//當(dāng)設(shè)置參數(shù)CrashIfDeallocating是true茁肠,即DoCrashIfDeallocating,如果newObj已經(jīng)被釋放或者newObj是一個(gè)不支持弱引用的類缩举,則暫停進(jìn)程
// deallocating或newObj的類不支持弱引用
// 當(dāng)設(shè)置參數(shù)CrashIfDeallocating是false垦梆,即DontCrashIfDeallocating,則存儲(chǔ)nil
enum CrashIfDeallocating {
    DontCrashIfDeallocating = false, DoCrashIfDeallocating = true
};
template <HaveOld haveOld, HaveNew haveNew,
          CrashIfDeallocating crashIfDeallocating>

這個(gè)函數(shù)在Clang中的聲明如下:

id objc_initWeak(id *object, id value);

前提條件object是一個(gè)有效的指針仅孩,尚未注冊(cè)為__weak對(duì)象托猩。value為null或指向有效對(duì)象的指針。

如果value是空指針或它指向的對(duì)象已經(jīng)釋放辽慕,則object也就是weak的指針將初始化為0(nil)京腥。 否則,將object注冊(cè)為指向value__weak對(duì)象溅蛉。 相當(dāng)于以下代碼:

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

舉個(gè)栗子:

NSObject *obj = [[NSObject alloc] init];

__weak NSObject *obj1 = obj;

就會(huì)編譯成

objc_initWeak(&obj1, obj);

objc_storeWeak

接下來看看objc_storeWeak方法


static id 
storeWeak(id *location, objc_object *newObj)
{
    assert(haveOld  ||  haveNew);
    if (!haveNew) assert(newObj == nil);
    // 初始化當(dāng)前正在 +initialize 的類對(duì)象為nil
    Class previouslyInitializedClass = nil;
    id oldObj;
    // 聲明新舊SideTable公浪,
    SideTable *oldTable;
    SideTable *newTable;

    // Acquire locks for old and new values.
    // Order by lock address to prevent lock ordering problems. 
    // Retry if the old value changes underneath us.
 retry:
    // 如果weak ptr之前弱引用過一個(gè)obj他宛,則將這個(gè)obj所對(duì)應(yīng)的SideTable取出,賦值給oldTable
    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;
    }

    //通過確保沒有弱引用的對(duì)象具有未初始化的 isa欠气,防止弱引用機(jī)制和 +initialize 機(jī)制之間的死鎖厅各。
    if (haveNew  &&  newObj) {
        // 獲得新對(duì)象的 isa 指針
        Class cls = newObj->getIsa();
        // 判斷 isa 非空且已經(jīng)初始化
        if (cls != previouslyInitializedClass  &&  
            !((objc_class *)cls)->isInitialized()) 
        {
            // 解鎖新舊SideTable
            SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
            _class_initialize(_class_getNonMetaClass(cls, (id)newObj));
            
            // 如果 newObj 已經(jīng)完成執(zhí)行完 +initialize 是最理想情況
            // 如果 newObj的 +initialize 仍然在線程中執(zhí)行
            // (也就是說newObj的 +initialize 正在調(diào)用 storeWeak 方法)
            // 通過設(shè)置previousInitializedClass以在重試時(shí)識(shí)別它。
            previouslyInitializedClass = cls;

            goto retry;
        }
    }

    // 清除舊值预柒,實(shí)際上是清除舊對(duì)象weak_table中的location
    if (haveOld) {
        weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
    }

    // 分配新值讯检,實(shí)際上是保存location到新對(duì)象的weak_table種
    if (haveNew) {
        newObj = (objc_object *)
            weak_register_no_lock(&newTable->weak_table, (id)newObj, location, 
                                  crashIfDeallocating);
        // 如果弱引用被釋放 weak_register_no_lock 方法返回 nil

        // 如果新對(duì)象存在,并且沒有使用TaggedPointer技術(shù)卫旱,在引用計(jì)數(shù)表中設(shè)置若引用標(biāo)記位
        if (newObj  &&  !newObj->isTaggedPointer()) {
            // 標(biāo)記新對(duì)象有weak引用人灼,isa.weakly_referenced = true;
            
            newObj->setWeaklyReferenced_nolock();
        }

        // 設(shè)置location指針指向newObj
        // 不要在其他地方設(shè)置 *location。 那會(huì)引起競(jìng)爭(zhēng)顾翼。
        *location = (id)newObj;
    }
    else {
        // No new value. The storage is not changed.
    }
    
    SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);

    return (id)newObj;
}

其實(shí)這個(gè)方法看的不是很明白......

weak_unregister_no_lock

/** 
 * 解除已經(jīng)注冊(cè)的弱引用.
 * 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 弱引用表.
 * @param referent 舊對(duì)象.
 * @param referrer 舊對(duì)象弱引用地址.
 */
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;

    // 如果其舊對(duì)象為 nil投放,無需注銷
    if (!referent) return;

    // 獲取對(duì)象對(duì)應(yīng)的weak_entry_t
    if ((entry = weak_entry_for_referent(weak_table, referent))) {
        // 通過地址來解除引用關(guān)聯(lián)
        remove_referrer(entry, referrer);
        
        // 判斷referrers是否為空
        bool empty = true;
        if (entry->out_of_line()  &&  entry->num_refs != 0) {
            empty = false;
        }
        else {
            for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
                if (entry->inline_referrers[i]) {
                    empty = false; 
                    break;
                }
            }
        }

        // 如果referrers為空,weak_table刪除entry
        if (empty) {
            weak_entry_remove(weak_table, entry);
        }
    }

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

weak_register_no_lock

/** 
 * 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 新對(duì)象
 * @param referrer weak變量地址
 */
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;

    // 如果新對(duì)象為nil 或 referent 采用了TaggedPointer計(jì)數(shù)方式适贸,直接返回灸芳,不做任何操作
    if (!referent  ||  referent->isTaggedPointer()) return referent_id;

    // 保證引用對(duì)象有效(沒有在析構(gòu),同時(shí)應(yīng)該支持weak引用)
    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);
    }
    // 正在析構(gòu)的對(duì)象拜姿,不能夠被弱引用烙样,如果crashIfDeallocating為true,crash 蕊肥,并輸出日志谒获。否者返回nil
    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;
    // 在 weak_table中找到referent對(duì)應(yīng)的weak_entry,并將referrer加入到weak_entry中
    if ((entry = weak_entry_for_referent(weak_table, referent))) {
        // 把weak變量地址,加入weak_entry_t
        // 先用數(shù)組inline_referrers存儲(chǔ)壁却,滿了用哈希表referrers
        // 如果referrers到了3/4容量批狱,就擴(kuò)容2倍,重新存回去
        // 沒滿展东,直接存入referrers赔硫,碰撞處理(哈希值++)
        append_referrer(entry, referrer);
    } 
    else {
        // 新建一個(gè)weak_entry_t,保存weak地址
        weak_entry_t new_entry(referent, referrer);
        // 如果weak_table滿容盐肃,進(jìn)行自增長(zhǎng)爪膊,到了3/4就擴(kuò)容1倍
        weak_grow_maybe(weak_table);
        // new_entry插入weak_table,碰撞處理(哈希值++)
        weak_entry_insert(weak_table, &new_entry);
    }

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

    // 返回新對(duì)象
    return referent_id;
}

weak_clear_no_lock

當(dāng)對(duì)象被釋放砸王,清除對(duì)象的weak_entry_t推盛、設(shè)置referrers中所有的weak地址設(shè)置為nil
/** 
 * 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) 
{
     //對(duì)象
    objc_object *referent = (objc_object *)referent_id;

    weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
    如果entry為nil,直接return
    if (entry == nil) {
        /// XXX shouldn't happen, but does with mismatched CF/objc
        //printf("XXX no entry for clear deallocating %p\n", referent);
        return;
    }

    // 將引用歸0
    weak_referrer_t *referrers;
    size_t count;
    
    // 獲取weak地址數(shù)組处硬,分為:inline_referrers與referrers
    // 前面講過只有4個(gè)元素的數(shù)組小槐,默認(rèn)情況下用inline_referrers來存儲(chǔ)弱引用的指針。當(dāng)大于4個(gè)的時(shí)候使用referrers來存儲(chǔ)指針
    if (entry->out_of_line()) {
        referrers = entry->referrers;
        count = TABLE_SIZE(entry);
    } 
    else {
        referrers = entry->inline_referrers;
        count = WEAK_INLINE_COUNT;
    }
    
    // 通過for循環(huán)取出每個(gè)weak ptr的地址
    for (size_t i = 0; i < count; ++i) {
        objc_object **referrer = referrers[i];
        if (referrer) {
            //// 如果weak ptr確實(shí)weak引用了referent,則將weak地址設(shè)置為nil
            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();
            }
        }
    }
    
    // 把referent的weak_entry_t也要移除出weak_table
    weak_entry_remove(weak_table, entry);
}



參考文章:
Clang 8 documentation
weak 弱引用的實(shí)現(xiàn)方式
Objective-C 引用計(jì)數(shù)原理
iOS管理對(duì)象內(nèi)存的數(shù)據(jù)結(jié)構(gòu)以及操作算法--SideTables凿跳、RefcountMap件豌、weak_table_t-一
細(xì)說weak
Runtime如何實(shí)現(xiàn)weak屬性?
Objective-C 引用計(jì)數(shù)原理
OC Runtime之Weak(1)---weak_table_t
OC Runtime之Weak(2)---weak_entry_t
OC 看objc源碼認(rèn)識(shí)weak
Objective-C runtime機(jī)制(6)——weak引用的底層實(shí)現(xiàn)原理

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末控嗜,一起剝皮案震驚了整個(gè)濱河市茧彤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌疆栏,老刑警劉巖曾掂,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異壁顶,居然都是意外死亡珠洗,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門若专,熙熙樓的掌柜王于貴愁眉苦臉地迎上來许蓖,“玉大人,你說我怎么就攤上這事调衰〔沧Γ” “怎么了?”我有些...
    開封第一講書人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我线衫,道長(zhǎng),這世上最難降的妖魔是什么矫户? 我笑而不...
    開封第一講書人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘爬早。我一直安慰自己,他們只是感情好启妹,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著醉旦,像睡著了一般饶米。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上车胡,一...
    開封第一講書人閱讀 49,741評(píng)論 1 289
  • 那天檬输,我揣著相機(jī)與錄音,去河邊找鬼匈棘。 笑死丧慈,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播逃默,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼鹃愤,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了完域?” 一聲冷哼從身側(cè)響起软吐,我...
    開封第一講書人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎吟税,沒想到半個(gè)月后凹耙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡肠仪,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年肖抱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片异旧。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡意述,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出泽艘,到底是詐尸還是另有隱情欲险,我是刑警寧澤,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布匹涮,位于F島的核電站天试,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏然低。R本人自食惡果不足惜喜每,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望雳攘。 院中可真熱鬧带兜,春花似錦、人聲如沸吨灭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽喧兄。三九已至无畔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間吠冤,已是汗流浹背浑彰。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來泰國(guó)打工拯辙, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留郭变,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像诉濒,于是被迫代替她去往敵國(guó)和親周伦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容