iOS-OC底層01:alloc init到底做了什么

OC對象

簡單案例來開啟我們的探索之旅

     MyPerson *p1 = [MyPerson alloc];
    MyPerson *p2 = [p1 init];
    MyPerson *p3 = [p1 init];
    LGNSLog(@"%@ - %p - %p",p1,p1,&p1);
    LGNSLog(@"%@ - %p - %p",p2,p2,&p2);
    LGNSLog(@"%@ - %p - %p",p3,p3,&p3);
打印結(jié)果
<MyPerson: 0x600001d86230> - 0x600001d86230 - 0x7ffeea0e50b8
<MyPerson: 0x600001d86230> - 0x600001d86230 - 0x7ffeea0e50b0
<MyPerson: 0x600001d86230> - 0x600001d86230 - 0x7ffeea0e50a8

為什么p1,p2,p3指向同一個對象呢预麸,又為什么指針地址不一樣呢滔驶。


MyPerson.png

探索底層的方法

我們知道蘋果有很多的庫,如果我們想探索對象形成的過程什燕,我們就要知道對象形成在哪里庫里粘勒,下面有三種方法
1.下符號斷點(diǎn)的形式直接跟流程
2.通過按住control+step into
3.匯編查看跟流程

1.下符號斷點(diǎn)的形式直接跟流程

image.png

輸入alloc
我們在MyPerson的初始化方法處打斷點(diǎn),另外我們在斷點(diǎn)走到MyPerson初始化方法之前先Disable Breakpoint,當(dāng)走到初始化方法時在Enable Breakpoint.因?yàn)樵贛yPerson初始化之前屎即,項目中有其他的alloc的調(diào)用庙睡,如果UIViewController等等
如下圖我們找到了alloc的庫。


image.png

2.通過按住control+step into

在MyPerson初始化方法處打斷點(diǎn)技俐,然后control+step into乘陪,我們可見


image.png

然后objc_alloc下符號斷點(diǎn),可見開源庫


image.png

3.匯編查看跟流程

通過下圖開啟匯編


image.png

在MyPerson初始化方法處打斷點(diǎn)雕擂,可見


image.png

然后取objc_alloc下符號斷點(diǎn)啡邑,下面操作和 通過按住control+step into相同.

定位objc源碼

蘋果開源源碼匯總: https://opensource.apple.com
這個地址用的更直接 https://opensource.apple.com/tarballs/
編譯源碼,可參考https://github.com/LGCooci/objc4_debug

image.png

alloc init到底做了什么

123.png

alloc 關(guān)鍵方法分析

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

static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
//此時allocWithZone為true井赌, checkNil為false 
{
#if __OBJC2__
    if (slowpath(checkNil && !cls)) return nil;
    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));
}

slowpath(x):(__builtin_expect(bool(x), 0))
fastpath(x):(__builtin_expect(bool(x), 1))

__builtin_expect 這個指令是gcc引入的谤逼,作用是允許程序員將最有可能執(zhí)行的分支告訴編譯器。這個指令的寫法為:__builtin_expect(EXP, N)仇穗。
意思是:EXP==N的概率很大流部。
cls->ISA()->hasCustomAWZ()

 bool hasCustomAWZ() const {
        return !cache.getBit(FAST_CACHE_HAS_DEFAULT_AWZ);
    }
  bool getBit(uint16_t flags) const {
        return _flags & flags;
    }
// class or superclass has default alloc/allocWithZone: implementation
// Note this is is stored in the metaclass.
#define FAST_CACHE_HAS_DEFAULT_AWZ    (1<<14)

類或超類具有默認(rèn)的alloc / allocWithZone:實(shí)現(xiàn) 注意,這存儲在元類中纹坐。
因?yàn)闆]有實(shí)現(xiàn)所以!cls->ISA()->hasCustomAWZ())判斷為true

計算申請內(nèi)存大小

extraBytes為0
    size = cls->instanceSize(extraBytes);

   size_t instanceSize(size_t extraBytes) const {
        if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
            return cache.fastInstanceSize(extraBytes);
        }

        size_t size = alignedInstanceSize() + extraBytes;
        // CF requires all objects be at least 16 bytes.
        if (size < 16) size = 16;
        return size;
    }

// __builtin_constant_p.
// Gcc的內(nèi)建函數(shù) __builtin_constant_p 用于判斷一個值是否為編譯時常數(shù)枝冀,如果參數(shù)EXP 的值是常數(shù),函數(shù)返回 1耘子,否則返回 0
    bool hasFastInstanceSize(size_t extra) const
    {
//因?yàn)閑xtra不是常數(shù)果漾,所以 _flags & FAST_CACHE_ALLOC_MASK
        if (__builtin_constant_p(extra) && extra == 0) {
            return _flags & FAST_CACHE_ALLOC_MASK16;
        }
        return _flags & FAST_CACHE_ALLOC_MASK;
    }

    size_t fastInstanceSize(size_t extra) const
    {
        ASSERT(hasFastInstanceSize(extra));

        if (__builtin_constant_p(extra) && extra == 0) {
            return _flags & FAST_CACHE_ALLOC_MASK16;
        } else {
            size_t size = _flags & FAST_CACHE_ALLOC_MASK;
            // remove the FAST_CACHE_ALLOC_DELTA16 that was added
            // by setFastInstanceSize
            return align16(size + extra - FAST_CACHE_ALLOC_DELTA16);
        }
    }
16禁止對齊。如果是17谷誓,(17 +15)%16*16 = 32跨晴,如果是16 
(16 +15)%16*16 = 16
static inline size_t align16(size_t x) {
    return (x + size_t(15)) & ~size_t(15);
}

開辟內(nèi)存空間

     obj = (id)calloc(1, size);

我們在這里打印obj ,po obj 結(jié)果是內(nèi)存地址0x000000010070cd40

關(guān)聯(lián)類

     obj->initInstanceIsa(cls, hasCxxDtor);

我們在這里打印obj 片林,po obj 結(jié)果 <LGPerson: 0x10070cd40>

探索NSObject 初始化方法

[NSObject alloc] 沒有調(diào)用alloc 方法是調(diào)用
objc_alloc(Class cls) ->callAlloc(cls, true, false);

[[NSObject alloc] init]調(diào)用也是沒有調(diào)用alloc方法
objc_alloc_init->[callAlloc(cls, true, false) init]

new 源碼探索

一般在開發(fā)中端盆,初始化除了init怀骤,還可以使用new,兩者本質(zhì)上并沒有什么區(qū)別焕妙,以下是objc中new的源碼實(shí)現(xiàn)蒋伦,通過源碼可以得知,new函數(shù)中直接調(diào)用了callAlloc函數(shù)(即alloc中分析的函數(shù))焚鹊,且調(diào)用了init函數(shù)痕届,所以可以得出new 其實(shí)就等價于 [alloc init]的結(jié)論

+ (id)new {
    return [callAlloc(self, false/*checkNil*/) init];
}

一般開發(fā)中并不建議使用new,主要是因?yàn)橛袝r會重寫init方法做一些自定義的操作末患,用new初始化可能會無法走到自定義的部分

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末研叫,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子璧针,更是在濱河造成了極大的恐慌嚷炉,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件探橱,死亡現(xiàn)場離奇詭異申屹,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)隧膏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進(jìn)店門哗讥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人胞枕,你說我怎么就攤上這事杆煞。” “怎么了腐泻?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵决乎,是天一觀的道長。 經(jīng)常有香客問我贫悄,道長瑞驱,這世上最難降的妖魔是什么娘摔? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任窄坦,我火速辦了婚禮,結(jié)果婚禮上凳寺,老公的妹妹穿的比我還像新娘鸭津。我一直安慰自己,他們只是感情好肠缨,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布逆趋。 她就那樣靜靜地躺著,像睡著了一般晒奕。 火紅的嫁衣襯著肌膚如雪闻书。 梳的紋絲不亂的頭發(fā)上名斟,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天,我揣著相機(jī)與錄音魄眉,去河邊找鬼砰盐。 笑死,一個胖子當(dāng)著我的面吹牛坑律,可吹牛的內(nèi)容都是我干的岩梳。 我是一名探鬼主播,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼晃择,長吁一口氣:“原來是場噩夢啊……” “哼冀值!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起宫屠,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤列疗,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后激况,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體作彤,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年乌逐,在試婚紗的時候發(fā)現(xiàn)自己被綠了竭讳。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡浙踢,死狀恐怖绢慢,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情洛波,我是刑警寧澤胰舆,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站蹬挤,受9級特大地震影響缚窿,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜焰扳,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一倦零、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧吨悍,春花似錦扫茅、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至躏仇,卻和暖如春恋脚,著一層夾襖步出監(jiān)牢的瞬間腺办,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工糟描, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留菇晃,地道東北人。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓蚓挤,卻偏偏與公主長得像磺送,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子灿意,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評論 2 348