iOS底層原理(三) isa分析

以下源碼分析基于 objc4-781

對象的isa初始化

+[NSObject alloc] 流程分析中淋硝,我們最終找到了對象創(chuàng)建的方法

static ALWAYS_INLINE id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
                              int construct_flags = OBJECT_CONSTRUCT_NONE,
                              bool cxxConstruct = true,
                              size_t *outAllocatedSize = nil)
{
    ASSERT(cls->isRealized());

    // Read class's info bits all at once for performance
    bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();
    bool hasCxxDtor = cls->hasCxxDtor();
    bool fast = cls->canAllocNonpointer();
    size_t size;

    size = cls->instanceSize(extraBytes);
    if (outAllocatedSize) *outAllocatedSize = size;

    id obj;
    if (zone) {
        obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
    } else {
        obj = (id)calloc(1, size);
    }
    if (slowpath(!obj)) {
        if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
            return _objc_callBadAllocHandler(cls);
        }
        return nil;
    }

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

    if (fastpath(!hasCxxCtor)) {
        return obj;
    }

    construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;
    return object_cxxConstructFromClass(obj, cls, construct_flags);
}

在上面的方法中主要做了3件事:

  • size = cls->instanceSize(extraBytes) 計(jì)算對象占用內(nèi)存的大小鳄抒。
  • obj = (id)calloc(1, size) 創(chuàng)建對象。
  • obj->initInstanceIsa(cls, hasCxxDtor) 初始化對象的 isa 谓谦。

id 類型

調(diào)用 calloc 方法將結(jié)果強(qiáng)轉(zhuǎn)成 id 類型芽世,在源碼中我們可以看到 id 類型的定義

// 定義在 Public Headers/objc.h
/// Represents an instance of a class.
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

// 定義在 Project Headers/objc-private.h 中 line 82
struct objc_object {
    isa_t isa;
}

/// A pointer to an instance of a class.
typedef struct objc_object *id;

我們可以在源碼中找到2個版本的 struct objc_object 定義今魔,一個在 Public Headers 目錄下私恬,另一個在 Project Headers 前者是暴露給開發(fā)者的,后者是內(nèi)部使用的柜裸,所以 id 類型本質(zhì)就是一個結(jié)構(gòu)體指針,struct objc_object 只有一個成員變量 isa_t isa掷漱。

調(diào)用 calloc 方法粘室,只是從系統(tǒng)中申請了一片內(nèi)存空間,這個時(shí)候這塊內(nèi)存空間中沒有任何內(nèi)容卜范,通過 obj->initInstanceIsa 初始化對象中的 isa

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

    initIsa(cls, true, hasCxxDtor);
}

initInstanceIsa 方法中調(diào)用了 initIsa(cls, true, hasCxxDtor)

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

intiIsa 方法中鹿榜,主要就是對 isa 進(jìn)行初始化海雪。

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

union 聯(lián)合體

我們發(fā)現(xiàn) isa_t 是一個聯(lián)合體,它和結(jié)構(gòu)體有什么區(qū)別舱殿?

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

結(jié)構(gòu)體是把不同的數(shù)據(jù)組合成一個整體奥裸,其變量是共存的,無論變量使用與否沪袭,都會分配內(nèi)存湾宙。

結(jié)構(gòu)體占用內(nèi)存為其所有成員占用內(nèi)存的總和。

  • 優(yōu)點(diǎn):存儲容量較大冈绊,包容性強(qiáng)侠鳄,成員之間不會互相影響。
  • 缺點(diǎn):所有屬性都會分配內(nèi)存死宣。
聯(lián)合體

聯(lián)合體也是由不同的數(shù)據(jù)類型組成的伟恶,但是其變量是互斥的,所有的成員占用同一段內(nèi)存毅该。而聯(lián)合體采用了內(nèi)存覆蓋技術(shù)博秫,同一時(shí)刻只能保存一個成員的值,如果對新的成員變量賦值眶掌,就會將原來的成員賦值挡育。

聯(lián)合體占用內(nèi)存為其最大成員占用的內(nèi)存。

  • 優(yōu)點(diǎn):所有成員共用一段內(nèi)存朴爬,使內(nèi)存的使用更為精細(xì)靈活即寒,同事也節(jié)省了內(nèi)存空間。
  • 缺點(diǎn):包容性弱寝殴。

union isa_t 結(jié)構(gòu)中蒿叠,有三個成員 Class cls uintptr_t bitsstruct { ISA_BITFIELD; } 他們占用內(nèi)存大小都是 8bytes,因此整個聯(lián)合體占用內(nèi)存 8bytes蚣常。

其中 struct { ISA_BITFIELD; } 是一個位域市咽,用來描述當(dāng)聯(lián)合體的值是 bits 是每一位所代表的意義

image.png

ISA_BITFIELD 是一段宏,展開后如上圖抵蚊。他有兩個版本施绎, __arm64__ 表示真機(jī)上位域的信息溯革, __x86_64__ 表示模擬器上的位域信息。

nonpointer

用來標(biāo)識 isa 是不是一個單純的 Class 指針

  • 0:isa 儲存一個 Class 指針地址谷醉。
  • 1: isa 不僅包含了 Class 指針地址致稀,還儲存了一下其他的信息。
has_assoc

用來標(biāo)識是不是又關(guān)聯(lián)對象

  • 0:沒有關(guān)聯(lián)對象
  • 1:存在關(guān)聯(lián)對象
has_cxx_dtor

標(biāo)識對象有沒有析構(gòu)器俱尼,如果沒有析構(gòu)函數(shù)抖单,可以更快的釋放

  • 0:沒有析構(gòu)器
  • 1:存在析構(gòu)器
shiftclx

用來儲存指針地址,在真機(jī)環(huán)境中最大的指針地址為 0x1000000000遇八,轉(zhuǎn)換成二進(jìn)制為 1 0000 0000 0000 0000 0000 0000 0000 0000 0000, 長度為37位矛绘, 且指針地址為8字節(jié)內(nèi)存對齊,由于內(nèi)存對齊的原因?qū)嶋H上 0x1000000000 也不是有效的地址刃永,低三位都為0货矮,所以指針的實(shí)際有效位數(shù)為33位。同樣的在模擬器中最大的指針地址為 0x7fffffe00000 對應(yīng)二進(jìn)制47位斯够, 除去低三位囚玫,有效的指針位數(shù)為44位。

magic

用于調(diào)試器判斷當(dāng)前對象是真正的對象读规,還是沒有初始化的空間抓督。

weakly_refrenced

表示對象是否指向或者曾經(jīng)指向一個ARC的弱引用變量。

deallocating

表示對象是否正在被釋放掖桦。

has_sidetable_rc

判斷該對象的引用對象是否過大本昏,如果過大則需要其他散列表來進(jìn)行存儲。

extra_rc

存放該對象的引用計(jì)數(shù)減1后的結(jié)果枪汪,如果引用計(jì)數(shù)超過1涌穆,會存在這個里面,如果引用計(jì)數(shù)為10雀久,extra_rc 的值就為9宿稀。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市赖捌,隨后出現(xiàn)的幾起案子祝沸,更是在濱河造成了極大的恐慌,老刑警劉巖越庇,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件罩锐,死亡現(xiàn)場離奇詭異,居然都是意外死亡卤唉,警方通過查閱死者的電腦和手機(jī)涩惑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來桑驱,“玉大人竭恬,你說我怎么就攤上這事跛蛋。” “怎么了痊硕?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵赊级,是天一觀的道長。 經(jīng)常有香客問我岔绸,道長理逊,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任亭螟,我火速辦了婚禮挡鞍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘预烙。我一直安慰自己,他們只是感情好道媚,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布扁掸。 她就那樣靜靜地躺著,像睡著了一般最域。 火紅的嫁衣襯著肌膚如雪谴分。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天镀脂,我揣著相機(jī)與錄音牺蹄,去河邊找鬼。 笑死薄翅,一個胖子當(dāng)著我的面吹牛沙兰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播翘魄,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼鼎天,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了暑竟?” 一聲冷哼從身側(cè)響起斋射,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎但荤,沒想到半個月后罗岖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡腹躁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年桑包,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片潜慎。...
    茶點(diǎn)故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡捡多,死狀恐怖蓖康,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情垒手,我是刑警寧澤蒜焊,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站科贬,受9級特大地震影響泳梆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜榜掌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一优妙、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧憎账,春花似錦套硼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至反砌,卻和暖如春雾鬼,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背宴树。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工策菜, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人酒贬。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓又憨,卻偏偏與公主長得像,于是被迫代替她去往敵國和親同衣。 傳聞我的和親對象是個殘疾皇子竟块,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評論 2 344