美文网首页
iOS 底层原理探索之alloc、init与new

iOS 底层原理探索之alloc、init与new

作者: Meek_L | 来源:发表于2020-09-07 13:35 被阅读0次

    前言:

    alloc、init、new 作为iOS开发者,是我们每天用到最多也可以说是最常见的代码之一,可是你知道它们到底都做了什么吗?下面我们就来深度探索一下!

    探索之前我们先来了解三种探索底层的方法:

    1.汇编分析: 菜单Debug -> Debug Workflow -> Always show Disassembly 选中之后会显示汇编编码

    2.下断点:control + in 跟进

    3.Symbolic Breakpoint 符号断点:

    97073995-B8C7-4161-82F1-B916B26DB9B2.png image.png

    alloc原理探索:

    1.首先我们先通过源码看一下大致的调用流程

    image

    2.接下来我们看一下具体源码的调用

    alloc

    + (id)alloc {
        return _objc_rootAlloc(self);
    }
    

    _objc_rootAlloc

    id
    _objc_rootAlloc(Class cls)
    {
        return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
    }
    

    callAlloc

    static ALWAYS_INLINE id
    callAlloc(Class cls, bool checkNil, bool allocWithZone=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));
    }
    

    _objc_rootAllocWithZone

    _objc_rootAllocWithZone(Class cls, malloc_zone_t *zone __unused)
    {
        // allocWithZone under __OBJC2__ ignores the zone parameter
        return _class_createInstanceFromZone(cls, 0, nil,
                                             OBJECT_CONSTRUCT_CALL_BADALLOC);
    }
    
    

    _class_createInstanceFromZone

    重点部分哦,前面通过运行源码我们一步步走到这里,终于开始要开辟空间了,是不是太不容易了,主要是下面这三个方法
    cls->instanceSize计算要申请的空间大小
    obj = (id)calloc(1, size);开辟内存的地方
    obj->initInstanceIsa 关联上对象(其实就是先建一个房子 写上是谁的名字)

    static ALWAYS_INLINE id
    _class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
                                  int construct_flags = OBJECT_CONSTRUCT_NONE,
                                  bool cxxConstruct = true,
                                  size_t *outAllocatedSize = nil)
    {
        ASSERT(cls->isRealized());
    
        // Read class's info bits all at once for performance
        bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();
        bool hasCxxDtor = cls->hasCxxDtor();
        bool fast = cls->canAllocNonpointer();
        size_t size;
        //计算内存大小
        size = cls->instanceSize(extraBytes);
        if (outAllocatedSize) *outAllocatedSize = size;
    
        id obj;
        if (zone) {
            obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
        } else {
            // alloc 申请开辟内存,返回地址指针
            obj = (id)calloc(1, size);
        }
        if (slowpath(!obj)) {
            if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
                return _objc_callBadAllocHandler(cls);
            }
            return nil;
        }
    
        if (!zone && fast) {
            //关联对象
            obj->initInstanceIsa(cls, hasCxxDtor);
        } else {
            // Use raw pointer isa on the assumption that they might be
            // doing something weird with the zone or RR.
            obj->initIsa(cls);
        }
    
        if (fastpath(!hasCxxCtor)) {
            return obj;
        }
    
        construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;
        return object_cxxConstructFromClass(obj, cls, construct_flags);
    }
    

    计算内存大小知识点:

    通过16字节对齐算法,以空间换时间概念

    static inline size_t align16(size_t x) {
        return (x + size_t(15)) & ~size_t(15);
    }
    

    alloc申请了空间那么init做了什么呢?

    接下来探索一下init源码,通过源码可知,inti的源码实现有以下两种

    类方法init
    + (id)init {
        return (id)self;
    }
    

    这里的init是一个构造方法 ,是通过工厂设计(工厂方法模式),主要是用于给用户提供构造方法入口。

    实例方法 init

    看一下实例init的代码

    MKPerson *objc = [[MKPerson alloc] init];
    

    通过main中的init跳转至init的源码实现

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

    跳转至_objc_rootInit的源码实现

    id
    _objc_rootInit(id obj)
    {
        // In practice, it will be hard to rely on this function.
        // Many classes do not properly chain -init calls.
        return obj;
    }
    

    new 源码探索

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

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

    但是一般开发中并不建议使用new,主要是因为有时会重写init方法做一些自定义的操作,例如 initWithXXX,会在这个方法中调用[super init],用new初始化可能会无法走到自定义的initWithXXX部分。
    关于new探索部分摘自:作者:Style_月月

    未完待续···不断学习,不断更新

    相关文章

      网友评论

          本文标题:iOS 底层原理探索之alloc、init与new

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