美文网首页
详细分析 alloc 流程

详细分析 alloc 流程

作者: 恬甜咖啡糖_0301 | 来源:发表于2020-02-29 16:43 被阅读0次

    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

    相关文章

      网友评论

          本文标题:详细分析 alloc 流程

          本文链接:https://www.haomeiwen.com/subject/ujjphhtx.html