美文网首页IOS开发知识点
iOS进阶专项分析(一)、alloc和init的底层实现

iOS进阶专项分析(一)、alloc和init的底层实现

作者: 溪浣双鲤 | 来源:发表于2020-06-18 08:50 被阅读0次

    引子:

    一个经典的面试问题:

    Objective-C中alloc和init的区别是什么?
    

    或者是问:下面这块代码执行后打印的结果

    BMPerson * person = [BMPerson alloc];
    BMPerson * p1 = [person init];
    BMPerson * p2 = [person init];
        
    NSLog(@"\n打印地址 %p - %p - %p", person, p1, p2);
    
    

    执行一下结果发现这三个对象的地址是相同的

    2020-06-17 15:47:20.525517+0800 TextProject[12922:914030] 
    打印地址 0x6000004b8680 - 0x6000004b8680 - 0x6000004b8680
    
    

    为什么呢?接下来我来详细分析一下alloc和init底层究竟干了些什么

    一、alloc底层探索

    点击alloc方法在NSObject.h文件中只能看到方法声明,从这个声明我们能看出 alloc这个方法是一个类方法,会返回一个 instancetype 类型的值。但是看不到具体信息了

    + (instancetype)alloc OBJC_SWIFT_UNAVAILABLE("use object initializers instead");
    
    

    想要看alloc底部具体实现,我们需要去苹果官方文档open source里面下载 objc开放的源码 苹果开放文档传送门

    下载完成打开objc4-750部分,直接搜索 alloc { 就能找到实现部分 (注意搜索输入的alloc{之间有空格)。

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

    发现了一个方法 _objc_rootAlloc ,继续点击该方法定义,找到 callAlloc

    // 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*/);
    }
    
    

    继续点击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 (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];
    }
    
    

    分析这部分代码,抛开优化的部分,我们不难挑出核心代码

    id obj = class_createInstance(cls, 0);
    return obj;
                
    

    继续进入方法 class_createInstance查看方法实现

    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();
    
        size_t size = cls->instanceSize(extraBytes);
        if (outAllocatedSize) *outAllocatedSize = size;
    
        id obj;
        if (!zone  &&  fast) {
            obj = (id)calloc(1, size);
            if (!obj) return nil;
            obj->initInstanceIsa(cls, hasCxxDtor);
        } 
        else {
            if (zone) {
                obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);
            } else {
                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.
            obj->initIsa(cls);
        }
    
        if (cxxConstruct && hasCxxCtor) {
            obj = _objc_constructOrFree(obj, cls);
        }
    
        return obj;
    }
    
    
    

    我们发现 _class_createInstanceFromZone里面 调用了cls->instanceSize calloc 以及 obj->initIsa , 即 开辟空间以及初始化isa, 至此 obj 初始化完成,直接把 obj 返回出去。

    到这里alloc的流程已经比较清楚了,alloc通过我们的类创建了实例对象

    二、init底层探索

    那么问题来了,既然alloc都已经把实例变量创建好了,为什么还需要再调用init呢?
    我们接下来在objc750源码中搜索 init {,发现了init这个实例方法调用了 _objc_rootInit 这个函数。

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

    分析一下这里的调用,加上我们平时创建实例都是类似于 [[BMPerson alloc]init],所以此时的self就是我们在alloc里已经创建完成的实例对象,既然知道了此时的 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.
        // 许多类没有属性 - 我们需要在init中按照业务需求自定义实现。
        return obj;
    }
    
    

    进入之后我们发现,_objc_rootInit这个方法里面其实什么也没做,直接把传入的self给返回了出去。接下来我们看注释,发现了苹果设计时候的用意。init 就是苹果提供给我们自定义的。这里体现了一个工厂设计模式父类没有执行,提供给子类自定义

    三、总结

    1、alloc&init底层调用流程图:

    alloc&init底层调用.png

    2、alloc方法底层已经完成开辟空间和创建类的实例变量了,init是专门为NSObject预留出来的,体现出工厂设计模式,即父类没有执行,提供给子类自定义

    溪浣双鲤的技术摸爬滚打之路

    相关文章

      网友评论

        本文标题:iOS进阶专项分析(一)、alloc和init的底层实现

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