详细分析 alloc 流程

0. 从main 入手-找一下感觉

在我们常见的app项目中,在main函数中打一个断点,并且再添加一个_objc_initd的符号断点,方便看调用栈状态。

调用栈状态

可以大概看出一个加载流程


image.png

1. 探索alloc

我们先就简单粗暴的来看看,新建一个TestNSObject 的类,然后 在alloc 处下一个断点,


截屏2020-02-29下午3.21.40.png

control + in (或者直接看汇编代码,或者符号断点)
最后可以看出,alloc最后其实是libObjc动态的objc_alloc

截屏2020-02-29下午5.02.37.png

2. 探索libObjc源码

通过断点分析,调用流程图如下:


NSObject-alloc流程2.png

配合核心源码分析

    static ALWAYS_INLINE id
    callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
    {
        if (slowpath(checkNil && !cls)) return nil;

    #if __OBJC2__
        if (fastpath(!cls->ISA()->hasCustomAWZ())) {
            // No alloc/allocWithZone implementation. Go straight to the allocator.
            // fixme store hasCustomAWZ in the non-meta class and 
            // add it to canAllocFast's summary
            if (fastpath(cls->canAllocFast())) {
                // No ctors, raw isa, etc. Go straight to the metal.
                bool dtor = cls->hasCxxDtor();
                id obj = (id)calloc(1, cls->bits.fastInstanceSize());
                if (slowpath(!obj)) return callBadAllocHandler(cls);
                obj->initInstanceIsa(cls, dtor);
                return obj;
            }
            else {
                // Has ctor or raw isa or something. Use the slower path.
                id obj = class_createInstance(cls, 0);
                if (slowpath(!obj)) return callBadAllocHandler(cls);
                return obj;
            }
        }
    #endif

        // No shortcuts available.
        if (allocWithZone) return [cls allocWithZone:nil];
        return [cls alloc];
    }

宏的说明

fastpath(x)表示x很可能不为0;slowpath(x)表示x很可能为0。——这里表示cls大概率是有值的,编译器可以不用每次都读取 return nil 指令,都是希望编译器进行优化

第二次 callAlloc 参数说明:
checkNil 是否需要判空直接传的是 false ,站在系统角度,前面已经在第一次调用 callAlloc 的时候进行了判空了,没必要再次进行判空的了。第三个参数allocWithZone 传的是 true ,关于这个方法,我查阅了苹果开发者文档,文档解释如下:

Do not override allocWithZone: to include any initialization code. Instead, class-specific versions of init... methods.
This method exists for historical reasons; memory zones are no longer used by Objective-C.
译:不要去重载 allocWithZone 并在其内部填充任何初始化代码,相反的,应该在 init... 里面进行类的初始化操作。
这个方法的存在是有历史原因的,内存 zone 已经不再被 Objective-C 所使用的。

按照苹果开发者文档的说法,其实 allocWithZone 本质上和 alloc 是没有区别的,只是在 Objective-C 远古时代,程序员需要使用诸如 allocWithZone 来优化对象的内存结构,而在当下,其实你写 alloc 和 allocWithZone 在底层是一样的。

方法里面两个if的判断

  1. if (fastpath(!cls->ISA()->hasCustomAWZ()))

    bool hasCustomAWZ() {
        return ! bits.hasDefaultAWZ();
    }
    
    bool hasDefaultAWZ() {
        return data()->flags & RW_HAS_DEFAULT_AWZ;
    }
    
    #define RW_HAS_DEFAULT_AWZ    (1<<16)
    
    struct class_rw_t {
        // Be warned that Symbolication knows the layout of this structure.
        uint32_t flags;
        ...省略
    }
    
    struct objc_class : objc_object {
        // Class ISA;
        Class superclass;
        cache_t cache;             // formerly cache pointer and vtable
        class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
    
        class_rw_t *data() { 
            return bits.data();
        }
        ...省略
    }
    

这里实际上是判断cls 也就是object_class 这一结构体内部的class_rw_tflags 与上一个宏RW_HAS_DEFAULT_AWZ 的结果。
测试结论:在第一次进入 callAlloc 方法内部的时候, flags 值为 1 ,然后 flags 与上 1<<16 结果就是 0 ,所以第一次就会跳过 if 里面的逻辑;而第二次进入 callAlloc 方法内部的时候, flags 值是一个很大的整数,与上 1<<16 后结果并不为0 ,就进入 if 里面的逻辑了。

  1. if (fastpath(cls->canAllocFast()))

     bool canAllocFast() {
         assert(!isFuture());
         return bits.canAllocFast();
     }
     
     bool canAllocFast() {
         return false;
     }
    

可以看出一直返回false,所以就走到了核心 class_createInstance-> _class_createInstanceFromZone

核心创建实例 _class_createInstanceFromZone 分析

static __attribute__((always_inline)) 
id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone, 
                              bool cxxConstruct = true, 
                              size_t *outAllocatedSize = nil)
{
    //一些常规的非空、以及容错处理
    if (!cls) return nil;
    assert(cls->isRealized());


    // Read class's info bits all at once for performance
    bool hasCxxCtor = cls->hasCxxCtor();
    bool hasCxxDtor = cls->hasCxxDtor();
    bool fast = cls->canAllocNonpointer();

    //获取cls的实例内存大小,extraBytes为0
    size_t size = cls->instanceSize(extraBytes);
    //outAllocatedSize 默认nil
    if (outAllocatedSize) *outAllocatedSize = size;

    id obj;
    //zone默认是nil,fast是true
    if (!zone  &&  fast) {
        //  根据实例size开辟内存
        obj = (id)calloc(1, size);
        if (!obj) return nil;
        //将cls和是否有c++析构器传入给initInstanceIsa,实例话isa
        obj->initInstanceIsa(cls, hasCxxDtor);
    } 
    else {
        //只有 allocWithZone 或 copyWithZone 会来到下面的逻辑
        if (zone) {
            // 根据给定的 zone 和 size 开辟内存
            obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);
        } else {
            // 根据 size 开辟内存
            obj = (id)calloc(1, size);
        }
        if (!obj) return nil;

        // Use raw pointer isa on the assumption that they might be 
        // doing something weird with the zone or RR.
        //初始化isa
        obj->initIsa(cls);
    }
    //如果有 C++ 初始化构造器和析构器,优化加速整个流程
    if (cxxConstruct && hasCxxCtor) {
        obj = _objc_constructOrFree(obj, cls);
    }

    return obj;
}

结论:
alloc 开辟申请内存空间
伴随初始化isa

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