美文网首页
iOS底层之alloc&init&new源码解析

iOS底层之alloc&init&new源码解析

作者: 当沉默已成习惯 | 来源:发表于2020-08-20 14:26 被阅读0次

    前言

    相信每一个iOS开发人员创建对象的时候都会写这样一行代码

    NSObject *obj = [[NSObject alloc] init];
    

    接下来就让已alloc为例来查找一下苹果的源码实现。

    一、查找alloc源码实现

    要想了解alloc的源码实现,咱们首先得找到alloc的源码在哪个库里实现,找到这个库以后先下载这个库,然后咱们就可以看他的底层源码实现了。
    找到alloc所在的库有以下三种方法:

    1、直接下代码断点

    先在alloc处打一个断点

    图一 然后按住control按钮点击下图(图二)中的红色方框内的in按钮 图二 然后会进入到调用栈会发现调用了objc_alloc 图三 继续control+in往下走走到objc_alloc内部就会找到alloc所在的库了 图四
    这里的 libobjc.A.dylib 这个动态链接库就是我们想要找的包含alloc源码的库了。

    2、下符号断点

    图五 图六
    通过上面图五、图六两步打一个alloc的符号断点,就可以看到alloc所在的动态链接库 图七 读取这个地址可以看到就是我们自己创建的类 图八

    注意:程序运行前请先取消alloc符号断点、现在自己写的代码的alloc出打一个断点,等程序断在此处后再选中alloc符号断点进行下一步就可以定位到图七中的动态库了。

    3、反汇编显示

    具体操作方式为打开 Debug 菜单下的 Debug Workflow 下的 Always Show Disassembly

    图九 然后control+in一步步下断点走到这 图十
    至此,我们得到了 alloc 实现位于 libObjc 这个动态库,而libObjc又属于objc4,而刚好苹果已经开源了这部分的代码,所以我们可以在苹果开源官网
    上下载即可。最新的 objc4 为 781。 图十一

    二、alloc流程分析

    alloc流程分析
    1. 点击对象的alloc方法进入源码实现
    //alloc源码分析-第一步
    + (id)alloc {
        return _objc_rootAlloc(self);
    }
    
    1. 进入查看_objc_rootAlloc源码实现
    // Base class implementation of +alloc. cls is not nil.
    // Calls [cls allocWithZone:nil].
    id
    _objc_rootAlloc(Class cls)
    {
        return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
    }
    
    1. 进入查看callAlloc源码实现
    // Call [cls alloc] or [cls allocWithZone:nil], with appropriate 
    // shortcutting optimizations.
    static ALWAYS_INLINE id
    callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
    {
    #if __OBJC2__
        if (slowpath(checkNil && !cls)) return nil;
        //类是否具有默认的alloc/allocWithZone:实现
        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));
    }
    

    一般情况下,在我们自定义的类中如果没有重写+(instance)alloc+(instance)allocWithZone:(struct _NSZone *)zone方法,默认走_objc_rootAllocWithZone这个方法

    1. 进入查看_objc_rootAllocWithZone源码实现
    id
    _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);
    }
    
    1. 进入查看_class_createInstanceFromZone源码实现
    /***********************************************************************
    * class_createInstance
    * fixme
    * Locking: none
    *
    * Note: this function has been carefully written so that the fastpath
    * takes no branch.
    **********************************************************************/
    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;
        // 1.计算需要开辟的内存大小,传入的extraBytes 为 0
        size = cls->instanceSize(extraBytes);
        if (outAllocatedSize) *outAllocatedSize = size;
    
        id obj;
        if (zone) {
            obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
        } else {
            // 2.申请内存
            obj = (id)calloc(1, size);
        }
        if (slowpath(!obj)) {
            if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
                return _objc_callBadAllocHandler(cls);
            }
            return nil;
        }
    
        // 3.将 cls类 与 obj指针(即isa) 关联
        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);
    }
    

    这部分源码为alloc的核心,由上面的源码可以看到主要流程分三步:

    • 计算需要开辟的内存的大小size = cls->instanceSize(extraBytes);
    • 申请内存obj = (id)calloc(1, size);
    • 将cls类与obj指针(即isa)关联obj->initInstanceIsa(cls, hasCxxDtor);

    三、init源码分析

    进入查看init源码实现

    // Replaced by CF (throws an NSException)
    + (id)init {
        return (id)self;
    }
    
    - (id)init {
        return _objc_rootInit(self);
    }
    
    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;
    }
    

    由上述源码可以看出init返回的是self本身,这样做的原因是为了用户可以重写此方法做一些初始化操作

    new 源码解析

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

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

    但是一般开发中并不建议使用new,主要是因为有时会重写init方法做一些初始化操作,用new创建对象可能会无法做到初始化一些东西。

    相关文章

      网友评论

          本文标题:iOS底层之alloc&init&new源码解析

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