isa與類(lèi)的關(guān)聯(lián)

alloc&init&new探索中我們知道瘪校,alloc一個(gè)對(duì)象主要有cls->instanceSize計(jì)算需要的內(nèi)存大小嗅榕、calloc(1, size)根據(jù)所需要的內(nèi)存大小開(kāi)辟內(nèi)存空間简十、initInstanceIsa(cls, hasCxxDtor)isa與內(nèi)關(guān)聯(lián)返帕;今天就來(lái)探索一下isa是如何與類(lèi)產(chǎn)生關(guān)聯(lián)的脓鹃。
準(zhǔn)備工作:objc4-781源碼

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);
    }
  • 跟進(jìn)initInstanceIsa(cls, hasCxxDtor)
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
    ASSERT(!cls->instancesRequireRawIsa());
    ASSERT(hasCxxDtor == cls->hasCxxDtor());

    initIsa(cls, true, hasCxxDtor);
}
  • 這我們看出忧设,接下來(lái)會(huì)執(zhí)行initIsa(cls, true, hasCxxDtor)
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;
    }
}

跟到這里我們可以看到一直困擾我們的isa是在這里完成配置的崔泵。 isa的類(lèi)型是isa_t。那么isa到到底是怎么配置的呢险污?

isa_t

我們跟進(jìn)isa_t發(fā)現(xiàn)isa_t是一個(gè)聯(lián)合體

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類(lèi)型使用聯(lián)合體是基于內(nèi)存優(yōu)化痹愚,采用char+位域(即二進(jìn)制中每一位均可以保存不同的信息)的原理。我們都知道isa指針占用8個(gè)字節(jié)蛔糯,即64位拯腮,可以合理的將64位利用起來(lái),極大的節(jié)約了內(nèi)存空間蚁飒。

  • 聯(lián)合體isa_t提供了兩個(gè)成員變量cls动壤、bits,由聯(lián)合體定義可知clsbits互斥淮逻。
  • 還提供了一個(gè)位域機(jī)構(gòu)體琼懊,在這個(gè)位域機(jī)構(gòu)體中是一個(gè)宏ISA_BITFIELD,根據(jù)釋義我們知道這個(gè)宏定義在isa.h
    • 位域結(jié)構(gòu)體
      按二進(jìn)制位來(lái)分配內(nèi)存的結(jié)構(gòu)體爬早,冒號(hào)表示位域肩碟。位域出現(xiàn)的原因是由于某些信息的存儲(chǔ)表示只需要幾個(gè)bit位就可以表示而不需要一個(gè)完整的字節(jié),同時(shí)也是為了節(jié)省存儲(chǔ)空間和方便處理

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

我們可以看出整個(gè)定義的聯(lián)合體位域無(wú)論是在__arm64__ (iOS)還是__x86_64__(macOS)中都占64位凸椿。32位這里就探索了削祈,一樣的原理。

  • nonpointer:是否對(duì)isa指針開(kāi)啟指針優(yōu)化脑漫。有兩個(gè)值髓抑,表示自定義的類(lèi)等,占1位
    • 0:純isa指針
    • 1:不只是類(lèi)對(duì)象地址优幸,isa中包含了類(lèi)信息吨拍、對(duì)象的引用計(jì)算等
  • has_assoc:是否有關(guān)聯(lián)對(duì)象
  • has_cxx_dtor:是否有C++的相關(guān)實(shí)現(xiàn)。表示該對(duì)象是否有C++/OC析構(gòu)器(類(lèi)似于dealloc)网杆,占1位
    • 如果有析構(gòu)函數(shù)羹饰,則需要做析構(gòu)相關(guān)的邏輯
    • 如果沒(méi)有伊滋,則可以快速釋放
  • shiftcls:存儲(chǔ)類(lèi)信息
    • __arm64__中占33位
    • __x86_64__中占44位
  • magic:用于調(diào)試器判斷當(dāng)前對(duì)象真的對(duì)象還是沒(méi)有初始化的空間 ,占6位
    • __arm64__: 0x1a
    • __x86_64__:0x3b
  • weakly_referenced:指對(duì)象是否被指向或者曾經(jīng)指向一個(gè)ARC的弱變量
    • 如果沒(méi)有可以更快釋放
  • deallocating:標(biāo)志對(duì)象是否正在釋放內(nèi)存
  • has_sidetable_rc:是否有外掛的散列表队秩。當(dāng)對(duì)象的引用計(jì)數(shù)大于10時(shí)笑旺,則需要借用該變量存儲(chǔ)進(jìn)位
  • extra_rc:額外的引用計(jì)數(shù),實(shí)際上是引用計(jì)數(shù)值減1馍资,也就是說(shuō)如果對(duì)象引用計(jì)數(shù)為10筒主,那么extra_rc是9.如果引用計(jì)數(shù)大于10,則需要用到has_sidetable_rc

isa

isa流程圖.png

從這張圖中我們可以看到類(lèi)的實(shí)例的isa指向了類(lèi)鸟蟹,類(lèi)isa指向的它的元類(lèi)乌妙,而元類(lèi)的isa指向了根元類(lèi)根元類(lèi)的isa還是指向本身建钥,但是根元類(lèi)NSObject也繼承自類(lèi)NSObject藤韵。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市熊经,隨后出現(xiàn)的幾起案子荠察,更是在濱河造成了極大的恐慌,老刑警劉巖奈搜,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件悉盆,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡馋吗,警方通過(guò)查閱死者的電腦和手機(jī)焕盟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)宏粤,“玉大人脚翘,你說(shuō)我怎么就攤上這事∩馨ィ” “怎么了来农?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)崇堰。 經(jīng)常有香客問(wèn)我沃于,道長(zhǎng),這世上最難降的妖魔是什么海诲? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任繁莹,我火速辦了婚禮,結(jié)果婚禮上特幔,老公的妹妹穿的比我還像新娘咨演。我一直安慰自己,他們只是感情好蚯斯,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布薄风。 她就那樣靜靜地躺著饵较,像睡著了一般。 火紅的嫁衣襯著肌膚如雪遭赂。 梳的紋絲不亂的頭發(fā)上循诉,一...
    開(kāi)封第一講書(shū)人閱讀 51,631評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音嵌牺,去河邊找鬼打洼。 笑死龄糊,一個(gè)胖子當(dāng)著我的面吹牛逆粹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播炫惩,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼僻弹,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了他嚷?” 一聲冷哼從身側(cè)響起蹋绽,我...
    開(kāi)封第一講書(shū)人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎筋蓖,沒(méi)想到半個(gè)月后卸耘,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡粘咖,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年蚣抗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瓮下。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡翰铡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出讽坏,到底是詐尸還是另有隱情锭魔,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布路呜,位于F島的核電站迷捧,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏胀葱。R本人自食惡果不足惜党涕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望巡社。 院中可真熱鬧膛堤,春花似錦、人聲如沸晌该。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至燕耿,卻和暖如春中符,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背誉帅。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工淀散, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蚜锨。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓档插,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親亚再。 傳聞我的和親對(duì)象是個(gè)殘疾皇子郭膛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355