細(xì)看objc-weak源碼

本文不看其他,只專注于weak的內(nèi)部結(jié)構(gòu)實(shí)現(xiàn)細(xì)節(jié)和源碼解讀,看了網(wǎng)上很多的文章都是貼上一篇open source里面的代碼,并沒有對(duì)實(shí)現(xiàn)細(xì)節(jié)進(jìn)行解釋。所以在這篇文章中,主要分為
weak_entry_t、weak_table_t的源碼解析,weak_entry_t和weak_table_t的相互關(guān)系,以及對(duì)應(yīng)的操作函數(shù)。

下文的主要是基于兩個(gè)對(duì)象來說的,一個(gè)是被引用的對(duì)象,一個(gè)是弱引用變量(也就是源代碼中大量出現(xiàn)的指向指針的指針)。

我說一下我源碼閱讀的習(xí)慣,先把目光放在頭文件中,因?yàn)轭^文件能夠給我們一個(gè)整體基礎(chǔ)結(jié)構(gòu)。弄清楚具體的結(jié)構(gòu)之后,然后再跳到實(shí)現(xiàn)文件中去看具體的實(shí)現(xiàn)細(xì)節(jié)。
先交代一下我的編譯環(huán)境和源代碼版本:

編譯環(huán)境:
Apple LLVM version 9.1.0 (clang-902.0.39.1)
Target: x86_64-apple-darwin17.5.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
源代碼版本:
objc4-723

頭文件類關(guān)系和結(jié)構(gòu)分析

我先根據(jù)頭文件畫一個(gè)基本的UML類圖:


UML類圖

DisguisedPtr模板類

先將視線放在weak_entry_t上面,結(jié)構(gòu)weak_entry_t的第一個(gè)成員變量是referent,它是一個(gè)DisguisedPtr類模板實(shí)例化之后的變量(點(diǎn)開前面的鏈接吧,不然我講不清楚,不然你會(huì)罵我的),這個(gè)成員其實(shí)就是保存被引用的對(duì)象。
DisguisedPtr類里面看起來這個(gè)類并不復(fù)雜,有一個(gè)uintptr_t類型的成員變量,由此DisguisedPtr類的對(duì)象所占用的內(nèi)存空間大小也應(yīng)該為8字節(jié)。
public下面主要是構(gòu)造函數(shù)加三大函數(shù)中的兩個(gè):重載復(fù)制運(yùn)算符,賦值構(gòu)造函數(shù);由于該類里面并沒有涉及到動(dòng)態(tài)new指針變量,所以其析構(gòu)函數(shù)便使用了默認(rèn)析構(gòu)函數(shù)。除此之外還重載一些其他的操作符。主要看一下私有的兩個(gè)成員函數(shù):

static uintptr_t disguise(T* ptr) {
  return -(uintptr_t)ptr;
}
static T* undisguise(uintptr_t val) {
  return (T*)-val;
}

其中disguise函數(shù)是將指針變量強(qiáng)轉(zhuǎn)為uintptr_t的整形變量,具體怎么偽裝呢?就是把該指針指向的內(nèi)存地址(16進(jìn)制數(shù)據(jù)比如:0x7ffeefbff4e8)強(qiáng)制轉(zhuǎn)換為無符號(hào)長(zhǎng)整型的十進(jìn)制數(shù)據(jù)。由于其類型是無符號(hào)長(zhǎng)整型,因此取負(fù)數(shù)是數(shù)據(jù)溢出之后取該類型取值范圍內(nèi)較大的長(zhǎng)整型值達(dá)到偽裝的效果(也就是不好去找到原內(nèi)存地址)。

unsigned long ul_val = 2;
unsigned long*bitl = &ul_val;
cout<<"ul_val address: "<<bitl<<endl;///0x7ffeefbff4e8
///140732920755432 取負(fù)數(shù) -> 18446744069408184208
cout<<"disguise: "<<disguise(bitl)<<endl;
cout<<"undisguise: "<<undisguise(*bitl)<<endl;/// 0xfffffffffffffffe 1111...1110

其作用在源文件的注釋中也說了,我通俗總結(jié)是:對(duì)那些比如leak這種內(nèi)存檢測(cè)工具進(jìn)行偽裝,然后這些檢測(cè)工具可能就不好去跟蹤被引用的對(duì)象。

weak_entry_t

現(xiàn)在來看一下union的具體內(nèi)存分布細(xì)節(jié),怎么來解釋這個(gè)問題呢?奉上objc-weak.h的源碼,打開源碼配合文章來看。

union {
        struct {/// 為了方便說明問題,我將該結(jié)構(gòu)取名為:struct1
            weak_referrer_t *referrers;
            uintptr_t        out_of_line : 2;
            uintptr_t        num_refs : PTR_MINUS_1;/// num_refs記錄的是實(shí)際引用數(shù)量
            uintptr_t        mask;/// 記錄當(dāng)前referrers數(shù)組容器的大小
            uintptr_t        max_hash_displacement;/// 根據(jù)hash-key尋找index的最大移動(dòng)數(shù),這個(gè)在后面的append_referrer會(huì)講
        };
        struct {/// 為了方便說明問題,我將該結(jié)構(gòu)取名為:struct2
            // out_of_line=0 is LSB of one of these (don't care which)
            weak_referrer_t  inline_referrers[WEAK_INLINE_COUNT];
        };
};

首先要有一個(gè)概念,union里面的多個(gè)成員是共享同一且相同大小的內(nèi)存空間,在strcut1結(jié)構(gòu)成員中算出其總共所占內(nèi)存大小為64*4,也就是32個(gè)字節(jié)。其中我的機(jī)器是64位機(jī),我的編譯器對(duì)于指針類型所占內(nèi)存大小的ABI實(shí)現(xiàn)為64位,而無符號(hào)長(zhǎng)整型占用的內(nèi)存大小也為64位。多說一句,在C++中結(jié)構(gòu)和類的內(nèi)存存儲(chǔ)區(qū)域好像都是在堆上面,由低地址向高地址生長(zhǎng)。
基于此來畫出inline_referrers和上面第一個(gè)結(jié)構(gòu)大致的內(nèi)存分布樣式(關(guān)于inline_referrers的元素類型模板類DisguisedPtr所占內(nèi)存大小在上面講DisguisedPtr類時(shí)提到了):



在源碼中注釋也說了:

// out_of_line_ness field overlaps with the low two bits of inline_referrers[1].
// inline_referrers[1] is a DisguisedPtr of a pointer-aligned address.
// The low two bits of a pointer-aligned DisguisedPtr will always be 0b00
// (disguised nil or 0x80..00) or 0b11 (any other address).
// Therefore out_of_line_ness == 0b10 is used to mark the out-of-line state.

out_of_line_ness是和inline_referrers[1]的低2位是等同的,out_of_line_nessnum_refs使用了位段,一共占用64位(2位和62位)。由于此時(shí)已經(jīng)是結(jié)構(gòu)內(nèi)存對(duì)齊了,所以下一個(gè)結(jié)構(gòu)成員mask的內(nèi)存地址就剛好換行。
上面還提到的0x0b10,它應(yīng)該是經(jīng)過DisguisedPtr偽裝之后得到的值,并不是實(shí)際的等于0b10,一個(gè)只占兩位內(nèi)存空間的,怎么也存儲(chǔ)不了16位的數(shù)據(jù)。out_of_line_ness == 0b10是標(biāo)記使用out-of-line的狀態(tài)。關(guān)于這個(gè)0b10我沒有想清楚它的由來,有知道的同學(xué)麻煩告知于我!!!
繼續(xù)來看該結(jié)構(gòu)的構(gòu)造函數(shù):

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

在創(chuàng)建weak_entry_t實(shí)例的時(shí)候,默認(rèn)是使用inline_referrers的方式來管理對(duì)象引用的,并把其余的位上的數(shù)據(jù)清空。
out_of_line_ness用來判斷使用out_of_line的方式來進(jìn)行引用管理,這個(gè)out_of_line_ness的值主要是依據(jù)于被引用的對(duì)象,其引用變量的個(gè)數(shù)決定的,具體的邏輯在下文會(huì)講到。
再看看struct1的referrers成員,看起來是一個(gè)指針變量,更具體的說是存儲(chǔ)的引用變量數(shù)組的起始地址,而這些引用變量指針指向的地址被DisguisedPtr進(jìn)行了偽裝。

到這里我把weak_entry_t的內(nèi)存分布講了一遍(具體的含義在上面代碼塊中的注釋里),然后下面來看一下weak_table_t

weak_table_t

weak_table_t在頭文件中看不出什么特別的內(nèi)容,但是從源碼中可以看出,應(yīng)該是一個(gè)基于C的結(jié)構(gòu),沒有使用C++中結(jié)構(gòu)獨(dú)有的特性。

struct weak_table_t {
    weak_entry_t *weak_entries;
    size_t    num_entries;/// 和weak_entry_t的num_refs概念類似
    uintptr_t mask;///和 weak_entry_t的mask概念類似
    uintptr_t max_hash_displacement;/// 和weak_entry_t的max_hash_displacement概念類似
};

同樣的,其weak_entries成員也應(yīng)該是一個(gè)數(shù)組,存儲(chǔ)著weak_entry_t變量的指針。針對(duì)該結(jié)構(gòu)頭文件中公開的操作函數(shù)有:

id weak_register_no_lock(weak_table_t *weak_table, id referent, 
                         id *referrer, bool crashIfDeallocating);
void weak_unregister_no_lock(weak_table_t *weak_table, id referent, id *referrer);
#if DEBUG
bool weak_is_registered_no_lock(weak_table_t *weak_table, id referent);
#endif
void weak_clear_no_lock(weak_table_t *weak_table, id referent);

這看不了什么具體的內(nèi)容,所以針對(duì)頭文件的解讀就到這里。下面去實(shí)現(xiàn)文件中看看具體的實(shí)現(xiàn),看看網(wǎng)上為什么都在說的基于Hash表的一個(gè)存儲(chǔ)結(jié)構(gòu)。源碼地址,老規(guī)矩,打開這個(gè)網(wǎng)頁(yè)對(duì)照著源碼來看。

objc-weak具體實(shí)現(xiàn)細(xì)節(jié)

在實(shí)現(xiàn)文件中,首先看兩個(gè)hash函數(shù):

static inline uintptr_t hash_pointer(objc_object *key);
static inline uintptr_t w_hash_pointer(objc_object **key);

它們會(huì)根據(jù)對(duì)象的指針(不管是指針還是指向指針的指針)調(diào)用一個(gè)fast-hash函數(shù)來生成一個(gè)key,其原理是基于fast_hash,而這個(gè)key的作用目前我們無從得知,但是在下面中,你會(huì)看到這個(gè)key作為散列表查找散列槽的重要依據(jù)。
第一個(gè)函數(shù)hash_pointer是對(duì)應(yīng)weak_table_t中weak_entries散列表的,也就是根據(jù)被引用對(duì)象生成的key;
第二個(gè)函數(shù)w_hash_pointer則是對(duì)應(yīng)weak_entry_t中out_of_line情況下referrers散列表的,也就是根據(jù)弱引用變量生成的key。

grow_refs_and_insert函數(shù)

繼續(xù)看源碼,下面主要來看看一個(gè)很重要的函數(shù):

__attribute__((noinline, used))
static void grow_refs_and_insert(weak_entry_t *entry, 
                                 objc_object **new_referrer)
{
    assert(entry->out_of_line);
    /**
      * #define TABLE_SIZE(entry) (entry->mask ? entry->mask + 1 : 0)
      * entry->mask用來記錄referrers的數(shù)量
      */
    size_t old_size = TABLE_SIZE(entry);
    size_t new_size = old_size ? old_size * 2 : 8;/// 增長(zhǎng)一倍的大小

    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_internal(TABLE_SIZE(entry), sizeof(weak_referrer_t));
    entry->num_refs = 0;
    entry->max_hash_displacement = 0;
    /// 開始處理數(shù)據(jù)
    for (size_t i = 0; i < old_size && num_refs > 0; i++) {
        if (old_refs[i] != nil) {
            append_referrer(entry, old_refs[i]);/// 把老數(shù)據(jù)復(fù)制進(jìn)新的entry里面
            num_refs--;
        }
    }
    // Insert
    append_referrer(entry, new_referrer);/// 給entry插入新的數(shù)據(jù)
    if (old_refs) _free_internal(old_refs);
}

由于基于C的數(shù)組其實(shí)都是定長(zhǎng)的,為了能夠動(dòng)態(tài)地增加新元素就需要不斷地去申請(qǐng)新的內(nèi)存空間,并且還要是連續(xù)的內(nèi)存地址(要是不連續(xù)的地址就去使用鏈表的方式,但是鏈表的索引明顯弱于數(shù)組的)。正是因?yàn)樾聞?dòng)態(tài)申請(qǐng)的連續(xù)內(nèi)存空間,這就需要把老數(shù)據(jù)復(fù)制過來,并把需要新增的數(shù)據(jù)也追加進(jìn)去,最后釋放掉原內(nèi)存空間:


它其實(shí)和C++里面的動(dòng)態(tài)數(shù)組的原理一樣,為了不頻繁地去申請(qǐng)(calloc)新的空間和頻繁地?cái)?shù)據(jù)移動(dòng)。所以每次2倍增長(zhǎng)來增加weak_entry_t的長(zhǎng)度。為什么說是C++里面動(dòng)態(tài)數(shù)組的做法,在《數(shù)據(jù)結(jié)構(gòu)與算法實(shí)現(xiàn)-C++描述》里有提及這些內(nèi)容。

append_referrer和remove_referrer

在grow_refs_and_insert函數(shù)中調(diào)用了append_referrer函數(shù),這個(gè)函數(shù)很明顯是做插入操作的,默認(rèn)使用inline的方式來增加新增的weak引用,如果使用inline的方式失敗了,則是以outline的方式,并申請(qǐng)對(duì)應(yīng)的存儲(chǔ)空間,把entry->referrers指向新申請(qǐng)的內(nèi)存地址,把inline_referrers數(shù)組里的數(shù)據(jù)拷貝到new_referrers中,其源碼如下:

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_internal(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 = REFERRERS_OUT_OF_LINE;
        entry->mask = WEAK_INLINE_COUNT-1;
        entry->max_hash_displacement = 0;
}

從這里就可以看出,當(dāng)被引用對(duì)象的弱引用referrers個(gè)數(shù)小于WEAK_INLINE_COUNT時(shí),其entry里面是以inline小數(shù)組方式來存儲(chǔ)這些弱引用變量的,只有當(dāng)inline_referrers全部裝滿之后,該entry out_of_line被設(shè)置為REFERRERS_OUT_OF_LINE,后續(xù)如若有變量繼續(xù)引用該對(duì)象則是以outline的方式存儲(chǔ)的。

union是在被引用變量的referrers個(gè)數(shù)小于等于WEAK_INLINE_COUNT時(shí),使用inline數(shù)組的內(nèi)存表現(xiàn)形式;當(dāng)referrers個(gè)數(shù)超過了WEAK_INLINE_COUNT則以struct1的內(nèi)存表現(xiàn)形式!

由于使用inline的方式是使用小數(shù)組的方式,但是針對(duì)弱引用對(duì)象過多,那么它的存取性能就是考慮的一個(gè)重點(diǎn)。而散列是一種用于以常數(shù)平均時(shí)間執(zhí)行插入、刪除和查找的技術(shù)。
下面這個(gè)過程我不是很確定,如有不同的建議希望指出。

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

begin是通過引用new_referrer調(diào)用散列函數(shù)獲取一個(gè)散列值,這個(gè)散列值就是散列表中的元素查找自己所在散列槽的key。
從源碼可以看出,通過散列值查找元素對(duì)應(yīng)散列槽的方式好像是使用了線性探測(cè)法。簡(jiǎn)化上面的代碼,配合下方這圖來看一下把new_referrer指針查找正確index的過程:


如上圖,假設(shè)使用w_hash_pointer獲取到的key為2,obj1成功插入到散列槽2中,obj2使用w_hash_pointer獲取到的key也為2,此時(shí)散列槽2已經(jīng)放入了obj1,那么只有正向地去尋找下一個(gè)散列槽,如果為空則放入obj2。
回到源碼中,在求begin值的時(shí)候。把hash值和entry->mask做了按位與的操作,但是這里為什么要對(duì)entry->mask做一次按位與操作呢?
entry->mask存儲(chǔ)著weak_entry_t的referrers數(shù)組大小,這樣做能保證所得的散列值是小于當(dāng)前數(shù)組的出界的,因?yàn)榇笥趓eferrers數(shù)組大小對(duì)應(yīng)的二進(jìn)制位的高位全部被置為0,從而避免出現(xiàn)數(shù)組越界帶來的問題。
關(guān)于出界和入界的概念,可以在《C陷阱與缺陷》中關(guān)于一個(gè)介紹for循環(huán)越界導(dǎo)致的死循環(huán)一節(jié),具體的記得不是很清楚了。針對(duì)出界這個(gè)概念還是蠻重要的,老板們可以去看一看。
remove_referrer和append_referrer在源碼上來看基本沒有什么區(qū)別,區(qū)別只不過是一個(gè)賦值,一個(gè)置空而已。

weak_table_t的擴(kuò)容和減容

針對(duì)weak_table_t的擴(kuò)容和減容源碼相對(duì)來說比較簡(jiǎn)單,限于篇幅我沒有提供對(duì)應(yīng)的代碼,所以在看的時(shí)候還麻煩自己打開上面提到的源碼地址對(duì)照來看。
在源碼中主要提供了如下函數(shù):

  • weak_entry_insert;
    函數(shù)weak_entry_insert和上一節(jié)提到的append_referrer是類似的,weak_table_t的內(nèi)部實(shí)現(xiàn)同樣也是使用散列表的方式來管理所有的entry變量的。只是weak_entry_insert沒有去嘗試inline的那一步。
  • weak_resize;
    函數(shù)weak_resize和上面提到的grow_refs_and_insert函數(shù)類似,在調(diào)整大小時(shí),都是創(chuàng)建一個(gè)新尺寸大小的內(nèi)存空間,然后將原內(nèi)存空間的數(shù)據(jù)移動(dòng)到新的內(nèi)存空間。weak_resize只有移動(dòng)老數(shù)據(jù),沒有新數(shù)據(jù)的添加!最后釋放掉原內(nèi)存。
  • weak_grow_maybe;
    函數(shù)weak_grow_maybe是在原weak_table的entry數(shù)量大于了weak_table數(shù)組容量的3/4時(shí),便調(diào)用weak_resize去擴(kuò)充容量到原數(shù)組容量的2倍。
  • weak_compact_maybe;
    函數(shù)weak_compact_maybe是用來收縮容量的,當(dāng)數(shù)組的容量?jī)?nèi)大部分都為空的話,則減容。
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
}
  • weak_entry_remove;
    函數(shù)weak_entry_remove用來weak_table_t的entries里對(duì)應(yīng)的entry
if (entry->out_of_line()) free(entry->referrers);
bzero(entry, sizeof(*entry));
weak_table->num_entries--;
weak_compact_maybe(weak_table);

sizeof(*entry)獲取到了weak_entry_t所占用的內(nèi)存大小,使用bzero是將該內(nèi)存段全部置為0,使用bzero而不使用memset影響并不大,使用memset需要多傳入一個(gè)參數(shù)來確定需要重置的值。在《Unix網(wǎng)絡(luò)編程》里創(chuàng)建sockaddr_in結(jié)構(gòu)變量時(shí),把對(duì)應(yīng)內(nèi)存空間數(shù)據(jù)清空用到了bzero,并講了一下和memset的區(qū)別,具體內(nèi)容可以去看看這本書。

頭文件暴露的四個(gè)函數(shù)

在頭文件中暴露了四個(gè)外部可用的函數(shù),分別是:weak_register_no_lock、weak_unregister_no_lock、weak_is_registered_no_lock和weak_clear_no_lock,根據(jù)注釋來看主要是針對(duì)weak_table_t的添加、刪除和清空數(shù)據(jù)等操作。在這里以下面的代碼為基礎(chǔ)來講解:

__weak id refer = obj;

下面再來具體看看這幾個(gè)函數(shù)在干什么?
weak_register_no_lock源代碼中提出,注冊(cè)一個(gè)新的鍵值對(duì),如果新的弱對(duì)象不存在則去新創(chuàng)建一個(gè)對(duì)應(yīng)的entry。

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

如果被弱引用指向的對(duì)象(obj)是isTaggedPointer,這里便不做相關(guān)操作,直接返回弱引用指向的對(duì)象(obj)。
關(guān)于什么是Tagged Pointer,后面我再去細(xì)看一下里面的源碼。從這里的源碼可以看出,如果是TaggedPointer就不做后續(xù)操作,因?yàn)橹羔槻]有指向真正的內(nèi)存地址,返回的值則是被引用對(duì)象自身。

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

如果存在對(duì)應(yīng)的entry則直接調(diào)用append_referrer進(jìn)行插入。如果不存在,則調(diào)用weak_entry_t的構(gòu)造函數(shù)創(chuàng)建一個(gè)新的對(duì)象,并查看是否需要針對(duì)weak_table進(jìn)行擴(kuò)容,將新的entry插入到weak_table中。下圖是一個(gè)為對(duì)象增加弱引用,并將引用添加到weak_table中的簡(jiǎn)易流程:


現(xiàn)在來看一下weak_unregister_no_lock函數(shù),針對(duì)weak_table的移除,必須確保entry已經(jīng)存在于weak_table中,才會(huì)去進(jìn)行后續(xù)的操作,同樣把對(duì)應(yīng)的流程圖畫出來:


最后兩個(gè)函數(shù)是一個(gè)是debug狀態(tài)下用于判斷某一entry是否存在于weak_table中,另一個(gè)函數(shù)則是對(duì)特定的被弱引用的對(duì)象(obj)的所有引用做清楚操作。

結(jié)語

到這里objc-weak應(yīng)該算是講清楚了(天知道我的表達(dá)能力怎么樣。。。),最后我從外層結(jié)構(gòu)到內(nèi)層結(jié)構(gòu)來一一總結(jié)下:
1、weak_table可以存儲(chǔ)多個(gè)entry,而且它會(huì)根據(jù)其散列表中entry的個(gè)數(shù)來伸縮其所占內(nèi)存大小;
2、一個(gè)entry表示的是一個(gè)被弱引用的對(duì)象(上文提到的obj),該變量可以被多個(gè)變量弱引用(refer)。所以entry也存在一個(gè)散列表,其用來存儲(chǔ)對(duì)應(yīng)的弱引用變量的引用。也就是前面源碼里面提到的指向指針的指針。
3、entry的out_of_line_ness只有在弱引用變量大于WEAK_INLINE_COUNT時(shí)才會(huì)置為REFERRERS_OUT_OF_LINE。也就是只有在這時(shí)候union才會(huì)使用struct1結(jié)構(gòu)內(nèi)存布局。
4、還有就是out_of_line_ness == 0b10沒有看懂。。。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,606評(píng)論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,582評(píng)論 3 418
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,540評(píng)論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,028評(píng)論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,801評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,223評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,294評(píng)論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,442評(píng)論 0 289
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,976評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,800評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,996評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,543評(píng)論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,233評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,926評(píng)論 1 286
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,702評(píng)論 3 392
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,991評(píng)論 2 374

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