001-alloc&init

前言

從一個對象的alloc開始,讓我們?nèi)隣C底層實(shí)現(xiàn)陕截,去探索學(xué)習(xí)OC源碼迹恐。

    LWPerson * obj = [LWPerson alloc];
    LWPerson * obj1 = [obj init];
    LWPerson * obj2 = [obj init];
    
    LWPerson * newObj = [LWPerson alloc];
 
    NSLog(@"%@---%p--%p",obj,obj,&obj);
    NSLog(@"%@---%p--%p",obj1,obj1,&obj1);
    NSLog(@"%@---%p--%p",obj2,obj2,&obj2);
    NSLog(@"%@---%p--%p",newObj,newObj,&newObj);
    <LWPerson: 0x281404710>---0x281404710--0x16b5cdbe0
    <LWPerson: 0x281404710>---0x281404710--0x16b5cdbd8
    <LWPerson: 0x281404740>---0x281404740--0x16b5cdbd0
截屏2021-07-05 下午10.02.42.png

初步總結(jié)

  • alloc具有開辟一塊內(nèi)存的功能,而init 沒有開辟內(nèi)存的功能
  • ps:棧區(qū) 開辟的內(nèi)存是高地址到低地址塘安,堆區(qū)則是低地址到高地址(這里會延伸出一個奇怪的問題)

三種探索底層的方法

  1. 符號斷點(diǎn)
  2. 匯編 Xcode -> Debug -> Debug Workflow -> Always Show Disassembly
  3. 斷點(diǎn) step into

源碼

alloc
+ (id)alloc {
    return _objc_rootAlloc(self);
}
_objc_rootAlloc
id _objc_rootAlloc(Class cls)
{
    return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
callAlloc
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
#if __OBJC2__ //判斷是不是 objc2.0版本
    //slowpath(x):x很可能為假糠涛,為真的概率很小 
    //fastpath(x):x很可能為真 
    //其實(shí)將fastpath和slowpath去掉是完全不影響任何功能,寫上是告訴編譯器對代碼進(jìn)行優(yōu)化
    if (slowpath(checkNil && !cls)) return nil;
    //判斷該類是否實(shí)現(xiàn)自自定義的 +allocWithZone,沒有則進(jìn)入if條件句
    if (fastpath(!cls->ISA()->hasCustomAWZ())) {
        return _objc_rootAllocWithZone(cls, nil);
    }
#endif

    // No shortcuts available.
    if (allocWithZone) {
        return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil);
    }
    return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc));
}
_objc_rootAllocWithZone
id
_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone __unused)
{
    // allocWithZone under __OBJC2__ ignores the zone parameter
    return _class_createInstanceFromZone(cls, 0, nil,
                                         OBJECT_CONSTRUCT_CALL_BADALLOC);
}

zone參數(shù)被丟棄兼犯,開發(fā)者即使自定義allocWithZone方法也無法指定想使用的zone忍捡,對象所在zone實(shí)際上由libmalloc庫分配。

對象創(chuàng)建核心函數(shù)_class_createInstanceFromZone
截屏2021-07-05 下午10.19.56.png
  • cls->instanceSize: 對象所需內(nèi)存大小
  • (id)calloc(1,size): 開辟內(nèi)存切黔,返回地址指針
  • obj->initInstanceIsa: 初始化isa指針砸脊,和類關(guān)聯(lián)起來
  • 返回obj(如果有cxxCtor,會調(diào)用初始化)

debug

        NSObject *n =  [NSObject alloc];
        LGPerson *p0 = [LGPerson alloc];
        LGPerson *p1  = [LGPerson alloc];
        NSLog(@"測試一下");

在creatInstance函數(shù)處打了斷點(diǎn)纬霞,分別打印三次alloc的堆棧

NSObject的alloc
截屏2021-07-05 下午10.44.34.png

流程:objc_alloc->callAloc->_objc_rootAllocWithZone->createInstance

LGPerson第一次alloc
截屏2021-07-05 下午10.47.45.png

流程:objc_alloc-> callAloc-> [NSObject alloc]-> _objc_rootAlloc->
callAlloc-> _objc_rootAllocWithZone->createInstance

LGPerson第二次alloc
截屏2021-07-05 下午10.48.41.png

流程:objc_alloc-> callAlloc-> _objc_rootAllocWithZone-> createInstance

幾個疑問

從函數(shù)椗Ч妫看,[ SomeClass alloc]方法調(diào)用险领,都被轉(zhuǎn)化為objc_alloc函數(shù)調(diào)用侨舆,發(fā)生了什么秒紧,誰做的?
image.png

通過查看LLVM源碼挨下,我們發(fā)現(xiàn)是編譯器把a(bǔ)lloc消息發(fā)送熔恢,轉(zhuǎn)化了objc_init函數(shù)調(diào)用。至于為什么臭笆?
我的理解是這里可以看做是編譯器把a(bǔ)lloc方法給hook了叙淌,在創(chuàng)建自定義類模板的第一個實(shí)例對象的時候,objc_alloc會再次發(fā)送alloc消息愁铺,調(diào)用alloc方法鹰霍。其他時候objc_alloc函數(shù)調(diào)用會直接走到createInstane函數(shù)。對比第一次alloc茵乱,這顯然減少后續(xù)對象開辟內(nèi)存空間時的函數(shù)調(diào)用茂洒。

// Calls [cls alloc].
id
objc_alloc(Class cls)
{
    return callAlloc(cls, true/*checkNil*/, false/*allocWithZone*/);
}
為什么自定義類的第一次alloc和第二次alloc的函數(shù)調(diào)用堆棧不同呢?
  • debug調(diào)試看到hasCustomAWZ()返回值不同瓶竭。我們第一次alloc多走了一段發(fā)送alloc消息的流程督勺,在這個過程中我們的自定義類被實(shí)現(xiàn),被初始化斤贰,setInitialized()函數(shù)中智哀,cache的_flag成員關(guān)于是有CustomAWZ的標(biāo)記位被重新賦值。
  • 除第一次外荧恍,后面alloc流程瓷叫,自定義類如果沒有CustomAWZ,就會直接走_(dá)objc_rootAllocWithZone送巡,不會再經(jīng)過消息發(fā)送赞辩,如果有就會發(fā)送allocWithZone:消息。
  • 這也側(cè)面說明授艰,為什么llvm會把a(bǔ)lloc消息發(fā)送轉(zhuǎn)換成objc_alloc,它既保證了我們類在第一次接收消息時被初始化,也避免了頻繁的alloc消息發(fā)送世落,直接進(jìn)去了對象空間開辟流程淮腾。
  • 看到這里是不是很期待我們消息發(fā)送流程,以及它是如何觸發(fā)自定義類的初始化屉佳。
為什么NSObject的第一次alloc跟自定義類的不同谷朝,沒有走alloc方法調(diào)用呢?

這里不多說了武花,上圖


截屏2021-07-06 下午4.02.52.png
  • 很顯然NSObject作為基類圆凰,在main函數(shù)之前,程序加載過程中体箕,libobjc就被觸發(fā)了NSObject的initialize方法专钉。這也是我們在main函數(shù)執(zhí)行[NSObject alloc]直接進(jìn)入_objc_rootAllocWithZone的原因挑童。
  • 這里需要注意的一點(diǎn)是,NSObjcet類是有自定義awz的,看下面注釋跃须,特殊處理了呀站叼。
  // Special cases:
    // - NSObject AWZ  class methods are default.
  • 看到這是不是對我們iOS程序的加載流程也很好奇了。

畫個alloc流程圖吧
截屏2021-07-06 下午6.08.40.png

總結(jié):

  • LLVM在編譯時把所有的alloc消息發(fā)送菇民,轉(zhuǎn)化為objc_alloc()
  • 自定義類的alloc流程尽楔,如果類沒有initialize,或者實(shí)現(xiàn)了customAWZ(都是customAWZ返回為true)第练,會觸發(fā)alloc消息發(fā)送阔馋,否則將會直接進(jìn)_objc_rootAlloc
  • 建議不要customAWZ,因為zone參數(shù)在底層實(shí)現(xiàn)是被丟棄的娇掏,實(shí)際分配zone不會被開發(fā)者決定(libMaolloc分配)呕寝,反而讓alloc調(diào)用流程變復(fù)雜。
  • 下篇拓展下Align16驹碍,對齊

init

new

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末壁涎,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子志秃,更是在濱河造成了極大的恐慌怔球,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件浮还,死亡現(xiàn)場離奇詭異竟坛,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)钧舌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進(jìn)店門担汤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人洼冻,你說我怎么就攤上這事崭歧。” “怎么了撞牢?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵率碾,是天一觀的道長。 經(jīng)常有香客問我屋彪,道長所宰,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任畜挥,我火速辦了婚禮仔粥,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己躯泰,他們只是感情好谭羔,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著斟冕,像睡著了一般口糕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上磕蛇,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天景描,我揣著相機(jī)與錄音,去河邊找鬼秀撇。 笑死超棺,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的呵燕。 我是一名探鬼主播棠绘,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼再扭!你這毒婦竟也來了氧苍?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤泛范,失蹤者是張志新(化名)和其女友劉穎让虐,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體罢荡,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赡突,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了区赵。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惭缰。...
    茶點(diǎn)故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖笼才,靈堂內(nèi)的尸體忽然破棺而出漱受,到底是詐尸還是另有隱情,我是刑警寧澤骡送,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布昂羡,位于F島的核電站,受9級特大地震影響各谚,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜到千,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一昌渤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧憔四,春花似錦膀息、人聲如沸般眉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽甸赃。三九已至,卻和暖如春冗酿,著一層夾襖步出監(jiān)牢的瞬間埠对,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工裁替, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留项玛,地道東北人。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓弱判,卻偏偏與公主長得像襟沮,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子昌腰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評論 2 355

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