OC底層原理05-isa結(jié)構(gòu)分析

該文章是對(duì)前一篇 OC底層原理01-alloc流程探索initInstanceIsa(給對(duì)象關(guān)聯(lián)isa)分支的一個(gè)拓展和深入

一、準(zhǔn)備工作

二、源碼里面找isa

2.1 繼續(xù)obj->initInstanceIsa(cls, hasCxxDtor);流程

if (!zone && fast) {
     obj->initInstanceIsa(cls, hasCxxDtor);
} else {
     // Use raw pointer isa on the assumption that they might be
     // doing something weird with the zone or RR.
     obj->initIsa(cls);
}
  • 我們自定義的類(lèi)都是走initInstanceIsa
  • 系統(tǒng)某一些類(lèi)才會(huì)調(diào)用initIsa

2.2 進(jìn)入initInstanceIsa

inline void 
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
    ASSERT(!cls->instancesRequireRawIsa());
    ASSERT(hasCxxDtor == cls->hasCxxDtor());

    initIsa(cls, true, hasCxxDtor);
}
inline void 
objc_object::initIsa(Class cls)
{
    initIsa(cls, false, false);
}
  • initInstanceIsa也會(huì)進(jìn)入initIsa
  • 它們第二個(gè)參數(shù)nonpointer不同
  • initInstanceIsa是傳的true,initIsa是傳的false

2.3 進(jìn)入initIsa

inline void 
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 
{ 
    ASSERT(!isTaggedPointer()); 
    
    if (!nonpointer) {
        isa = isa_t((uintptr_t)cls);
    } else {
        ASSERT(!DisableNonpointerIsa);
        ASSERT(!cls->instancesRequireRawIsa());

        isa_t newisa(0);

#if SUPPORT_INDEXED_ISA
        ASSERT(cls->classArrayIndex() > 0);
        newisa.bits = ISA_INDEX_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
        newisa.bits = ISA_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.shiftcls = (uintptr_t)cls >> 3;
#endif

        // This write must be performed in a single store in some cases
        // (for example when realizing a class because other threads
        // may simultaneously try to use the class).
        // fixme use atomics here to guarantee single-store and to
        // guarantee memory order w.r.t. the class index table
        // ...but not too atomic because we don't want to hurt instantiation
        isa = newisa;
    }
}
  • 由于我們自定義的類(lèi)nonpointer都是true,所以我們會(huì)進(jìn)入else流程
  • 發(fā)現(xiàn)目標(biāo)isa,它是一個(gè)isa_t類(lèi)型的對(duì)象
  • isa_t newisa(0) 初始化一個(gè)isa
  • newisa.bits = hasCxxDtor給屬性bits賦值
  • newisa.has_cxx_dtor = hasCxxDtor給屬性has_cxx_dtor賦值
  • newisa.shiftcls = (uintptr_t)cls >> 3給屬性shiftcls賦值,并向右移三位
  • isa = newisa把新建的newisa賦值給對(duì)象的isa,進(jìn)行關(guān)聯(lián)

2.4 查看isa_t

union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };
#endif
};
  • isa_t是一個(gè)聯(lián)合體,屬性互斥。
  • clsbits 不能同時(shí)賦值

2.5 查看ISA_BITFIELD

# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
#   define ISA_MAGIC_MASK  0x000003f000000001ULL
#   define ISA_MAGIC_VALUE 0x000001a000000001ULL
#   define ISA_BITFIELD                                                      \
      uintptr_t nonpointer        : 1;                                       \
      uintptr_t has_assoc         : 1;                                       \
      uintptr_t has_cxx_dtor      : 1;                                       \
      uintptr_t shiftcls          : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
      uintptr_t magic             : 6;                                       \
      uintptr_t weakly_referenced : 1;                                       \
      uintptr_t deallocating      : 1;                                       \
      uintptr_t has_sidetable_rc  : 1;                                       \
      uintptr_t extra_rc          : 19
#   define RC_ONE   (1ULL<<45)
#   define RC_HALF  (1ULL<<18)

# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
#   define ISA_MAGIC_MASK  0x001f800000000001ULL
#   define ISA_MAGIC_VALUE 0x001d800000000001ULL
#   define ISA_BITFIELD                                                        \
      uintptr_t nonpointer        : 1;                                         \
      uintptr_t has_assoc         : 1;                                         \
      uintptr_t has_cxx_dtor      : 1;                                         \
      uintptr_t shiftcls          : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
      uintptr_t magic             : 6;                                         \
      uintptr_t weakly_referenced : 1;                                         \
      uintptr_t deallocating      : 1;                                         \
      uintptr_t has_sidetable_rc  : 1;                                         \
      uintptr_t extra_rc          : 8
#   define RC_ONE   (1ULL<<56)
#   define RC_HALF  (1ULL<<7)

# else
#   error unknown architecture for packed isa
# endif
  • 如果是arm64就走上面,如果是x86_64就走下面
  • 因?yàn)槲覀兣渲玫?code>objc4是基于macos的,所以我們要研究的是下面
  • isa存在內(nèi)存地址的第一段,也就是8字節(jié),8*8=64位

2.6 斷點(diǎn)分析newisa的賦值過(guò)程

2.6.1 newisa.bits = ISA_MAGIC_VALUE在這一行下斷點(diǎn)
image.png
2.6.2 打印newisa
(lldb) p newisa
(isa_t) $2 = {
  cls = nil
  bits = 0
   = {
    nonpointer = 0
    has_assoc = 0
    has_cxx_dtor = 0
    shiftcls = 0
    magic = 0
    weakly_referenced = 0
    deallocating = 0
    has_sidetable_rc = 0
    extra_rc = 0
  }
}
  • 初始都為空
2.6.3 斷點(diǎn)往下走一步,再次打印newisa
(lldb) p newisa
(isa_t) $3 = {
  cls = 0x001d800000000001
  bits = 8303511812964353
   = {
    nonpointer = 1
    has_assoc = 0
    has_cxx_dtor = 0
    shiftcls = 0
    magic = 59
    weakly_referenced = 0
    deallocating = 0
    has_sidetable_rc = 0
    extra_rc = 0
  }
}
  • cls被賦值了0x001d800000000001
  • bits 被賦值了8303511812964353
  • nonpointer被賦值了1
  • magic被賦值了59
2.6.3.1 系統(tǒng)給cls賦值的是什么
image.png
  • ISA_BITFIELD上面一個(gè)宏# define ISA_MAGIC_VALUE 0x001d800000000001ULL,這一步可以看做把ISA_MAGIC_VALUE直接賦值給cls,與打印臺(tái)打印的cls不是同一個(gè),這里的 clsnewisa 中的 cls
  • 這里的 ULLunsigned long long 無(wú)符號(hào)長(zhǎng)整型,只是一個(gè)類(lèi)型標(biāo)識(shí)
2.6.3.2 系統(tǒng)給bits賦值的是什么
image.png
  • 原來(lái)系統(tǒng)把宏ISA_MAGIC_VALUE的值0x001d800000000001轉(zhuǎn)成了10進(jìn)制,賦給了newisa中的cls
  • 注意:上圖中打印的cls,是objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)方法傳入的,為了驗(yàn)證當(dāng)前研究的是GomuPerson的流程
2.6.3.3 系統(tǒng)給nonpointer賦值的是什么
  • 因?yàn)?code>initIsa方法傳入的是一個(gè)true,所以賦值為1
2.6.3.4 系統(tǒng)給magic賦值的是什么
image.png
  • p/t命令將0x001d800000000001轉(zhuǎn)成2進(jìn)制打印,得上以上截圖的一串?dāng)?shù)字
  • 由宏ISA_BITFIELD的位域定義可以得到,magic占位是6位從第47位到第52位,由于是iOS小端,從右到左讀取,用分隔符每4位分開(kāi)方便查看0b0000_0000_0001_1101_1000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0001,得到第47位到52位為:111011
  • 1110112進(jìn)制,轉(zhuǎn)成10進(jìn)制得到59
2.6.4 斷點(diǎn)繼續(xù)往下走一步,再次打印newisa
(lldb) p newisa
(isa_t) $7 = {
  cls = 0x001d800000000005
  bits = 8303511812964357
   = {
    nonpointer = 1
    has_assoc = 0
    has_cxx_dtor = 1
    shiftcls = 0
    magic = 59
    weakly_referenced = 0
    deallocating = 0
    has_sidetable_rc = 0
    extra_rc = 0
  }
}
  • has_cxx_dtor被賦值了1,前面?zhèn)鬟^(guò)來(lái)hasCxxDtortrue
2.6.4 斷點(diǎn)繼續(xù)往下走一步,再次打印newisa
(lldb) p newisa
(isa_t) $8 = {
  cls = GomuPerson
  bits = 8303516107940741
   = {
    nonpointer = 1
    has_assoc = 0
    has_cxx_dtor = 1
    shiftcls = 536872048
    magic = 59
    weakly_referenced = 0
    deallocating = 0
    has_sidetable_rc = 0
    extra_rc = 0
  }
}
  • cls的值變成了GomuPerson,說(shuō)明isaGomuPerson已關(guān)聯(lián)
  • bits的值由8303511812964357變成了8303516107940741
  • shiftcls被賦值了536872048
2.6.4.1 shiftcls賦的值是什么
image.png
  • 因?yàn)?code>newisa.shiftcls = (uintptr_t)cls >> 3 這一步代碼,將cls 強(qiáng)轉(zhuǎn)成 uintptr_t (無(wú)符號(hào)長(zhǎng)整型的別名),然后右移位
  • 打印 (uintptr_t)cls 得到 4294976384
  • 4294976384 右移三位,得到 536872048
  • 這一步的目的就是將cls強(qiáng)轉(zhuǎn)后右移三位的值賦給shiftcls
  • 因?yàn)?code>newisa中又給shiftcls賦了值,所以bits必然會(huì)變
2.6.4.2 bits的值為什么變了

[圖片上傳中...(image.png-902d6d-1599838772142-0)]

  • bits當(dāng)前的值換算成2進(jìn)制打印,得到上圖$27
  • shiftcls的值換算成2進(jìn)制打印,得到上圖$28
  • $28前面補(bǔ)零,補(bǔ)足44位,則與$27完全相同
  • 說(shuō)明isa3-46位的確存的是cls,但是是右移了3位cls
2.6.5 反向由person的首地址isa推導(dǎo)出shiftcls
2.6.5.1 打印personisa的地址,轉(zhuǎn)成10進(jìn)制
image.png
  • 得到8303516107940741,很上面newisa中的 bits 完全一樣
2.6.5.2 由isa的地址推導(dǎo)給shiftcls賦值之前的(uintptr_t)cls
image.png
  • 由于shiftclsisa內(nèi)存的3-46位,所以我們要先抹掉前面3位,右移 >> 3位
  • 左邊空出20位,左移 << 20位,再右移 >> 17位,把左邊17位抹零
  • 得到結(jié)果4294976384,和給shiftcls賦值的(uintptr_t)cls的值一模一樣
2.6.5.3 shiftcls賦值的時(shí)候?qū)?code>(uintptr_t)cls右移三位的目的
  • 需要將一個(gè)64位的(uintptr_t)cls賦值給一個(gè)從isa第三位開(kāi)始長(zhǎng)度只有44的shiftcls
  • 那么我們需要取(uintptr_t)cls0-43位賦值給shiftcls,因?yàn)?code>shiftcls是從isa的第三位開(kāi)始,這樣賦值的后果是shiftclsisa中的值就會(huì)向左偏移移3位,
  • 為了保證賦值后的shiftcls(uintptr_t)cls相等,我們必須把(uintptr_t)cls3-46賦值給shiftcls,即(uintptr_t)cls>>3
2.6.6 isa的地址 & ISA_MASK,會(huì)得到 GomuPerson
image.png
  • 經(jīng)驗(yàn)證,isa的地址 & ISA_MASK,確定會(huì)得到 GomuPerson
  • ISA_MASK轉(zhuǎn)成二進(jìn)制,發(fā)現(xiàn)只有中間3-46位有效,所以isa與上ISA_MASK,相當(dāng)于把前3位和后17位置0

2.7 基于x86_64的位域圖

image.png
  • nonpointer:表示是否對(duì)isa指針開(kāi)啟指針優(yōu)化,占1位
    • 0:純isa指針
    • 1:不只是類(lèi)對(duì)象地址isa中包含了類(lèi)信息對(duì)象的引用計(jì)數(shù)
  • has_assoc:表示關(guān)聯(lián)對(duì)象標(biāo)志位,占1位
    • 0:沒(méi)有關(guān)聯(lián)對(duì)象
    • 1:存在關(guān)聯(lián)對(duì)象
  • has_cxx_dtor:表示該對(duì)象是否有C++/OC的析構(gòu)器dealloc),占1位
    • 0:沒(méi)有析構(gòu)函數(shù),則可以更快的釋放對(duì)象
    • 1:有析構(gòu)函數(shù),則需要做析構(gòu)邏輯
  • shiftclx 表示存儲(chǔ)類(lèi)的指針的值(類(lèi)的地址), 即類(lèi)信息
    • arm64中占 33位,開(kāi)啟指針優(yōu)化的情況下,在arm64架構(gòu)中有33位用來(lái)存儲(chǔ)類(lèi)指針
    • x86_64中占 44
  • magic:用于調(diào)試器判斷當(dāng)前對(duì)象是真的對(duì)象還是沒(méi)有初始化的空間,占6位
  • weakly_refrenced:表示對(duì)象是否被指向 或者 曾經(jīng)指向一個(gè)ARC弱變量,沒(méi)有弱引用的對(duì)象可以更快釋放,占1位
  • deallocating:標(biāo)志對(duì)象是是否正在釋放內(nèi)存,占1位
  • has_sidetable_rc:表示當(dāng)對(duì)象引用計(jì)數(shù)大于10時(shí),則需要借用該變量 存儲(chǔ)進(jìn)位,占1位
  • extra_rc:(額外的引用計(jì)數(shù)) --- 導(dǎo)尿管表示該對(duì)象的引用計(jì)數(shù)值,實(shí)際上是引用計(jì)數(shù)值減1,如果對(duì)象的引用計(jì)數(shù)為10,那么extra_rc9,占8位

四、拓展知識(shí)

構(gòu)造數(shù)據(jù)類(lèi)型的方式:

  • 結(jié)構(gòu)體 [struct]

結(jié)構(gòu)體是指把不同的數(shù)據(jù)組合成一個(gè)整體,其變量是共存的,變量不管是否使用,都會(huì)分配內(nèi)存
優(yōu)點(diǎn):存儲(chǔ)容量較大包容性強(qiáng),且成員之間不會(huì)相互影響
缺點(diǎn):所有屬性都分配內(nèi)存,比較浪費(fèi)內(nèi)存,假設(shè)有4個(gè)int成員,一共分配了16字節(jié)的內(nèi)存,但是在使用時(shí),你只使用了4字節(jié),剩余的12字節(jié)就是屬于內(nèi)存的浪費(fèi)

  • 聯(lián)合體 [union]

聯(lián)合體也是由不同的數(shù)據(jù)類(lèi)型組成,但其變量是互斥的,所有的成員共占一段內(nèi)存。而且聯(lián)合體采用了內(nèi)存覆蓋技術(shù)同一時(shí)刻只能保存一個(gè)成員的值,如果對(duì)新的成員賦值,就會(huì)將原來(lái)成員的值覆蓋掉
優(yōu)點(diǎn):所有成員共用一段內(nèi)存,使內(nèi)存的使用更為精細(xì)靈活,同時(shí)也節(jié)省了內(nèi)存空間
缺點(diǎn):包容性弱

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