OC底层探索03-常用的alloc,init,new到底做了什么?

前言:想必大家对于[xxx alloc] init]非常熟悉了,都知道是创建一个xxx的对象,但是OC底层到底做了什么?

首先看下方代码:

    HRTest * t = [HRTest alloc];
    HRTest * tt = [t init];
    HRTest * ttt = [t init];
    HRTest * tttt = [HRTest alloc];
    NSLog(@"%@---%p---%p",t,t,&t);
    NSLog(@"%@---%p---%p",tt,tt,&tt);
    NSLog(@"%@---%p---%p",ttt,ttt,&ttt);
    NSLog(@"%@---%p---%p",tttt,tttt,&tttt);
  • 输出结果:


常识:
  1. %p/t :是指向对象的指针 %p/&t :是指向对象指针的指针
  2. 栈区存放原则:从高位到低位; 堆区存放原则:从低位到高位
  3. 内存地址是连续的

  • 根据观察得出若干结论:
    1. 经过alloc之后获得了不同的内存空间,经过init之后内存空间相同。
      推断:内存空间是由alloc负责申请,从这个角度看init并没处理任何动作
    2. 对象是存放在堆区; 对象的指针存放在栈区

对象的存储位置

用一张图来解释:


alloc

alloc

想要一探alloc是如何申请了内存空间的,就需要使用上篇中提到的objc源码了。废话不多说,打开源码,加上断点,一步步开始调试:
此处有两种可能,简述流程省略代码:

  1. 创建NSObjec
    直接进入alloc流程
  2. 创建继承自NSObject的自定义类
    先进入_objc_alloc->callAlloc->alloc,为什么会进入_objc_alloc而不是调用的alloc这就要涉及到llvm中的知识,后续有机会再来解释,可以简单理解为llvm做了一次类似于hook的操作,将alloc转为_objc_alloc
  3. 最终到达了_class_createInstanceFromZone,这个方法是正在来进行内存申请操作的地方
    alloc流程图

_class_createInstanceFromZone

_class_createInstanceFromZone流程图
init
//此处只放出最核心代码
_class_createInstanceFromZone(...){
    ...
    size_t size;
    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;
        if (size < 16) size = 16;
        return size;
    }
    
size_t fastInstanceSize(size_t extra) const
    {...
    //计算实际内存占用
    size_t size = _flags & FAST_CACHE_ALLOC_MASK;
     return align16(size + extra - FAST_CACHE_ALLOC_DELTA16);
    }
    
static inline size_t align16(size_t x) {
    //著名的字节对齐算法
    return (x + size_t(15)) & ~size_t(15);
}
calloc
//此处只放出最核心代码
_class_createInstanceFromZone(...){
    ...
    id obj;
    if (zone) {
        obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
    } else {
        // 申请1块size大小的内存空间
        obj = (id)calloc(1, size);
    }
}
  • zone方式:在iOS8以后就基本不使用了
  • 注意calloc返回的是一个id,表示当前并无类型
initInstanceIsa
//此处只放出最核心代码
_class_createInstanceFromZone(...){
    ...
    id obj;

    if (!zone && fast) {
        //一般会进入此判断
        obj->initInstanceIsa(cls, hasCxxDtor);
    } else {
        obj->initIsa(cls);
    }
}

objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 
{ 
    ...
    //进行类型赋值
    isa = isa_t((uintptr_t)cls);
}

看完全部流程是不是感觉到流程索然繁杂,但是本质并不复杂,[狗头]

核心步骤:计算内存大小 - 申请内存 - 进行类的关联

fastpath、slowpath

在源码中反复出现的这两个宏定义,我觉得有必要简单解释一下:

//x很可能为真, fastpath 可以简称为 真值判断
#define fastpath(x) (__builtin_expect(bool(x), 1)) 
//x很可能为假,slowpath 可以简称为 假值判断
#define slowpath(x) (__builtin_expect(bool(x), 0)) 
  • 来源:__builtin_expect命令是由gcc引入的
  • 目的:提醒编译器可以对此处代码进行编译优化,以减少指令跳转带来的性能消耗
  • 作用:在编译过程中就允许程序员将最有可能执行到的代码分支告诉编译器

1,自定义类第一次callAlloc时没有找到默认的allocWithZone,经过objc_msgsend(alloc)之后,第二次callAlloc时找到了默认的allocWithZone。allocWithZone是什么时候创建加载的呢?

init做了什么

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

id _objc_rootInit(id obj)
{
    return obj;
}
  • 事实上init方法并没有做任何事情,也应证之前的猜想:
    内存空间是由alloc负责申请,从这个角度看init并没处理任何动作
  • apple苹果这样设计的目的:类似工厂方法,为后续自定义做一些处理提供一个入口

new做了什么

一般在开发中,初始化除了init,还会使用new,通过源码来看两者本质上并没有什么区别

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

但是在一般的开发中,如果使用自定的类,这里并不建议使用new,因为这里系统只会调用init方法,对于自定义的initWhitXXX并不会调用。但是系统自己类大可放心使用.

  • initWhitCustom并没有调用

参考资料:
fastpath slowpath
iOS 内存字节对齐

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