對(duì)象是如何初始化的(iOS)

關(guān)注倉(cāng)庫(kù),及時(shí)獲得更新:iOS-Source-Code-Analyze

Follow: Draveness · Github

在之前遗契,我們已經(jīng)討論了非常多的問題了溶弟,關(guān)于 objc 源代碼系列的文章也快結(jié)束了,其實(shí)關(guān)于對(duì)象是如何初始化的這篇文章本來是我要寫的第一篇文章胖翰,但是由于有很多前置內(nèi)容不得不說臼节,所以留到了這里撬陵。

+ alloc- init 這一對(duì)我們?cè)?iOS 開發(fā)中每天都要用到的初始化方法一直困擾著我, 于是筆者仔細(xì)研究了一下 objc 源碼中 NSObject 如何進(jìn)行初始化。

在具體分析對(duì)象的初始化過程之前网缝,我想先放出結(jié)論巨税,以免文章中的細(xì)枝末節(jié)對(duì)讀者的理解有所影響;整個(gè)對(duì)象的初始化過程其實(shí)只是為一個(gè)分配內(nèi)存空間粉臊,并且初始化 isa_t 結(jié)構(gòu)體的過程草添。

alloc 方法分析

先來看一下 + alloc 方法的調(diào)用棧(在調(diào)用棧中省略了很多不必要的方法的調(diào)用):open

id _objc_rootAlloc(Class cls)
└── static id callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
    └── id class_createInstance(Class cls, size_t extraBytes)
             └── id _class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone, bool cxxConstruct, size_t *outAllocatedSize)
            ├── size_t instanceSize(size_t extraBytes)
            ├── void    *calloc(size_t, size_t)
            └── inline void objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)

這個(gè)調(diào)用棧中的方法涉及了多個(gè)文件中的代碼,在下面的章節(jié)中會(huì)對(duì)調(diào)用的方法逐步進(jìn)行分析扼仲,如果這個(gè)調(diào)用棧讓你覺得很頭疼远寸,也不是什么問題促王。

alloc 的實(shí)現(xiàn)

+ (id)alloc {
    return _objc_rootAlloc(self);
}

alloc 方法的實(shí)現(xiàn)真的是非常的簡(jiǎn)單, 它直接調(diào)用了另一個(gè)私有方法 id _objc_rootAlloc(Class cls)

id _objc_rootAlloc(Class cls) {
    return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}

這就是上帝類 NSObject 對(duì) callAlloc 的實(shí)現(xiàn),我們省略了非常多的代碼而晒,展示了最常見的執(zhí)行路徑:

static id callAlloc(Class cls, bool checkNil, bool allocWithZone=false) {
    id obj = class_createInstance(cls, 0);
    return obj;
}

id class_createInstance(Class cls, size_t extraBytes) {
    return _class_createInstanceFromZone(cls, extraBytes, nil);
}

對(duì)象初始化中最重要的操作都在 _class_createInstanceFromZone 方法中執(zhí)行:

static id _class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone, bool cxxConstruct = true, size_t *outAllocatedSize = nil) {
    size_t size = cls->instanceSize(extraBytes);

    id obj = (id)calloc(1, size);
    if (!obj) return nil;
    obj->initInstanceIsa(cls, hasCxxDtor);

    return obj;
}

對(duì)象的大小

在使用 calloc 為對(duì)象分配一塊內(nèi)存空間之前蝇狼,我們要先獲取對(duì)象在內(nèi)存的大小:

size_t instanceSize(size_t extraBytes) {
    size_t size = alignedInstanceSize() + extraBytes;
    if (size < 16) size = 16;
    return size;
}

uint32_t alignedInstanceSize() {
    return word_align(unalignedInstanceSize());
}

uint32_t unalignedInstanceSize() {
    assert(isRealized());
    return data()->ro->instanceSize;
}

實(shí)例大小 instanceSize 會(huì)存儲(chǔ)在類的 isa_t 結(jié)構(gòu)體中倡怎,然后經(jīng)過對(duì)齊最后返回迅耘。

Core Foundation 需要所有的對(duì)象的大小都必須大于或等于 16 字節(jié)。

在獲取對(duì)象大小之后监署,直接調(diào)用 calloc 函數(shù)就可以為對(duì)象分配內(nèi)存空間了颤专。

isa 的初始化

在對(duì)象的初始化過程中除了使用 calloc 來分配內(nèi)存之外,還需要根據(jù)類初始化 isa_t 結(jié)構(gòu)體:

inline void objc_object::initIsa(Class cls, bool indexed, bool hasCxxDtor) { 
    if (!indexed) {
        isa.cls = cls;
    } else {
        isa.bits = ISA_MAGIC_VALUE;
        isa.has_cxx_dtor = hasCxxDtor;
        isa.shiftcls = (uintptr_t)cls >> 3;
    }
}

上面的代碼只是對(duì) isa_t 結(jié)構(gòu)體進(jìn)行初始化而已:

union isa_t {
   isa_t() { }
   isa_t(uintptr_t value) : bits(value) { }
    
   Class cls;
   uintptr_t bits;
    
   struct {
       uintptr_t indexed           : 1;
       uintptr_t has_assoc         : 1;
       uintptr_t has_cxx_dtor      : 1;
       uintptr_t shiftcls          : 44;
       uintptr_t magic             : 6;
       uintptr_t weakly_referenced : 1;
       uintptr_t deallocating      : 1;
       uintptr_t has_sidetable_rc  : 1;
       uintptr_t extra_rc          : 8;
   };
};

在這里并不想過多介紹關(guān)于 isa_t 結(jié)構(gòu)體的內(nèi)容钠乏,你可以看從 NSObject 的初始化了解 isa 來了解你想知道的關(guān)于 isa_t 的全部?jī)?nèi)容栖秕。

init 方法

NSObject- init 方法只是調(diào)用了 _objc_rootInit 并返回了當(dāng)前對(duì)象:

- (id)init {
    return _objc_rootInit(self);
}

id _objc_rootInit(id obj) {
    return obj;
}

總結(jié)

在 iOS 中一個(gè)對(duì)象的初始化過程很符合直覺,只是分配內(nèi)存空間晓避、然后初始化 isa_t 結(jié)構(gòu)體簇捍,其實(shí)現(xiàn)也并不復(fù)雜,這篇文章也是這個(gè)系列文章中較為簡(jiǎn)單并且簡(jiǎn)短的一篇俏拱。

關(guān)注倉(cāng)庫(kù)暑塑,及時(shí)獲得更新:iOS-Source-Code-Analyze

Follow: Draveness · Github

原文鏈接: http://draveness.me/object-init

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市锅必,隨后出現(xiàn)的幾起案子事格,更是在濱河造成了極大的恐慌,老刑警劉巖搞隐,帶你破解...
    沈念sama閱讀 211,423評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件驹愚,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡劣纲,警方通過查閱死者的電腦和手機(jī)逢捺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,147評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來味廊,“玉大人蒸甜,你說我怎么就攤上這事棠耕∮喾穑” “怎么了?”我有些...
    開封第一講書人閱讀 157,019評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵窍荧,是天一觀的道長(zhǎng)辉巡。 經(jīng)常有香客問我,道長(zhǎng)蕊退,這世上最難降的妖魔是什么郊楣? 我笑而不...
    開封第一講書人閱讀 56,443評(píng)論 1 283
  • 正文 為了忘掉前任憔恳,我火速辦了婚禮,結(jié)果婚禮上净蚤,老公的妹妹穿的比我還像新娘钥组。我一直安慰自己,他們只是感情好今瀑,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,535評(píng)論 6 385
  • 文/花漫 我一把揭開白布程梦。 她就那樣靜靜地躺著,像睡著了一般橘荠。 火紅的嫁衣襯著肌膚如雪屿附。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,798評(píng)論 1 290
  • 那天哥童,我揣著相機(jī)與錄音挺份,去河邊找鬼。 笑死贮懈,一個(gè)胖子當(dāng)著我的面吹牛匀泊,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播朵你,決...
    沈念sama閱讀 38,941評(píng)論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼探赫,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了撬呢?” 一聲冷哼從身側(cè)響起伦吠,我...
    開封第一講書人閱讀 37,704評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎魂拦,沒想到半個(gè)月后毛仪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,152評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡芯勘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,494評(píng)論 2 327
  • 正文 我和宋清朗相戀三年箱靴,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片荷愕。...
    茶點(diǎn)故事閱讀 38,629評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡衡怀,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出安疗,到底是詐尸還是另有隱情抛杨,我是刑警寧澤,帶...
    沈念sama閱讀 34,295評(píng)論 4 329
  • 正文 年R本政府宣布荐类,位于F島的核電站怖现,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜屈嗤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,901評(píng)論 3 313
  • 文/蒙蒙 一潘拨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧饶号,春花似錦铁追、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至透硝,卻和暖如春狰闪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背濒生。 一陣腳步聲響...
    開封第一講書人閱讀 31,978評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工埋泵, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人罪治。 一個(gè)月前我還...
    沈念sama閱讀 46,333評(píng)論 2 360
  • 正文 我出身青樓丽声,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親觉义。 傳聞我的和親對(duì)象是個(gè)殘疾皇子雁社,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,499評(píng)論 2 348

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