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);
打印结果
<MyPerson: 0x600001d86230> - 0x600001d86230 - 0x7ffeea0e50b8
<MyPerson: 0x600001d86230> - 0x600001d86230 - 0x7ffeea0e50b0
<MyPerson: 0x600001d86230> - 0x600001d86230 - 0x7ffeea0e50a8

为什么p1,p2,p3指向同一个对象呢,又为什么指针地址不一样呢。


MyPerson.png

探索底层的方法

我们知道苹果有很多的库,如果我们想探索对象形成的过程,我们就要知道对象形成在哪里库里,下面有三种方法
1.下符号断点的形式直接跟流程
2.通过按住control+step into
3.汇编查看跟流程

1.下符号断点的形式直接跟流程

image.png

输入alloc
我们在MyPerson的初始化方法处打断点,另外我们在断点走到MyPerson初始化方法之前先Disable Breakpoint,当走到初始化方法时在Enable Breakpoint.因为在MyPerson初始化之前,项目中有其他的alloc的调用,如果UIViewController等等
如下图我们找到了alloc的库。


image.png

2.通过按住control+step into

在MyPerson初始化方法处打断点,然后control+step into,我们可见


image.png

然后objc_alloc下符号断点,可见开源库


image.png

3.汇编查看跟流程

通过下图开启汇编


image.png

在MyPerson初始化方法处打断点,可见


image.png

然后取objc_alloc下符号断点,下面操作和 通过按住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 关键方法分析

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引入的,作用是允许程序员将最有可能执行的分支告诉编译器。这个指令的写法为:__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)

类或超类具有默认的alloc / allocWithZone:实现 注意,这存储在元类中。
因为没有实现所以!cls->ISA()->hasCustomAWZ())判断为true

计算申请内存大小

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的内建函数 __builtin_constant_p 用于判断一个值是否为编译时常数,如果参数EXP 的值是常数,函数返回 1,否则返回 0
    bool hasFastInstanceSize(size_t extra) const
    {
//因为extra不是常数,所以 _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);
}

开辟内存空间

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

我们在这里打印obj ,po obj 结果是内存地址0x000000010070cd40

关联类

     obj->initInstanceIsa(cls, hasCxxDtor);

我们在这里打印obj ,po obj 结果 <LGPerson: 0x10070cd40>

探索NSObject 初始化方法

[NSObject alloc] 没有调用alloc 方法是调用
objc_alloc(Class cls) ->callAlloc(cls, true, false);

[[NSObject alloc] init]调用也是没有调用alloc方法
objc_alloc_init->[callAlloc(cls, true, false) init]

new 源码探索

一般在开发中,初始化除了init,还可以使用new,两者本质上并没有什么区别,以下是objc中new的源码实现,通过源码可以得知,new函数中直接调用了callAlloc函数(即alloc中分析的函数),且调用了init函数,所以可以得出new 其实就等价于 [alloc init]的结论

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

一般开发中并不建议使用new,主要是因为有时会重写init方法做一些自定义的操作,用new初始化可能会无法走到自定义的部分

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350