前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >OC底层探索03-常用的alloc,init,new到底做了什么?OC底层探索03-常用的alloc,init,new到底做了什么?

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

作者头像
用户8893176
发布2021-08-09 11:26:18
7780
发布2021-08-09 11:26:18
举报
文章被收录于专栏:小黑娃Henry
前言:想必大家对于[xxx alloc] init]非常熟悉了,都知道是创建一个xxx的对象,但是OC底层到底做了什么?

首先看下方代码:

代码语言:javascript
复制
    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
代码语言:javascript
复制
//此处只放出最核心代码
_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
代码语言:javascript
复制
//此处只放出最核心代码
_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
代码语言:javascript
复制
//此处只放出最核心代码
_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

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

代码语言:javascript
复制
//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做了什么

代码语言:javascript
复制
- (id)init {
    return _objc_rootInit(self);
}

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

new做了什么

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

代码语言:javascript
复制
+ (id)new {
    retur [callAlloc(self, false/*checkNil*/) init];
}

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

  • initWhitCustom并没有调用

参考资料:

fastpath slowpath

iOS 内存字节对齐

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/9/11 上,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言:想必大家对于[xxx alloc] init]非常熟悉了,都知道是创建一个xxx的对象,但是OC底层到底做了什么?
  • 对象的存储位置
  • alloc
    • alloc
      • _class_createInstanceFromZone
        • init
        • calloc
        • initInstanceIsa
        • 核心步骤:计算内存大小 - 申请内存 - 进行类的关联
      • fastpath、slowpath
      • init做了什么
      • new做了什么
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档