系统底层源码分析(11)——alloc、init、new

我们知道alloc、init、new就是开辟内存空间初始化对象,今天就来探讨一下它们分别在底层干了什么。

一. alloc

首先简单创建项目,调用alloc并断点:

然后进入汇编模式运行(Debug -> Debug Workflow -> Always Show Disassembly):

我们发现原来调用alloc时,底层会调用objc_alloc。也许下面代码可以说明:

(也可从LLVM源码探究调用alloc会走objc_alloc)

  1. objc4-750源码探究:
// Calls [cls alloc].
id
objc_alloc(Class cls)
{
    return callAlloc(cls, true/*checkNil*/, false/*allocWithZone*/);
}
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())) { ... }//canAllocFast只返回false
        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];//先走objc_alloc然后走到这,接着才真正调用alloc
}
+ (id)alloc {
    return _objc_rootAlloc(self);
}
id
_objc_rootAlloc(Class cls)
{
    return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);//传参不一样
}
  1. 这里再次调用callAlloc,然而这次会走class_createInstance
id 
class_createInstance(Class cls, size_t extraBytes)
{
    return _class_createInstanceFromZone(cls, extraBytes, nil);
}
static __attribute__((always_inline)) 
id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone, 
                              bool cxxConstruct = true, 
                              size_t *outAllocatedSize = nil)
{
    ...
    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 { ... }
    ...
    return obj;
}
  1. 来到这里,通过instanceSize函数获取开辟内存空间大小:
struct objc_class : objc_object {
    ...
    // May be unaligned depending on class's ivars.
    uint32_t unalignedInstanceSize() {
        assert(isRealized());
        return data()->ro->instanceSize;//返回大小
    }

    // Class's ivar size rounded up to a pointer-size boundary.
    uint32_t alignedInstanceSize() {
        return word_align(unalignedInstanceSize());
    }

    size_t instanceSize(size_t extraBytes) {
        size_t size = alignedInstanceSize() + extraBytes;
        // CF requires all objects be at least 16 bytes.
        if (size < 16) size = 16;//预留字节,保证安全
        return size;
    }
    ...
}
#   define WORD_MASK 7UL

static inline uint32_t word_align(uint32_t x) {
    return (x + WORD_MASK) & ~WORD_MASK;//内存对齐,变成8的倍数
}

最后是通过ro返回大小,我们可以看一下ro

struct class_rw_t {
    ...
    const class_ro_t *ro;
    ...
}
struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif

    const uint8_t * ivarLayout;
    
    const char * name;
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;

    method_list_t *baseMethods() const {
        return baseMethodList;
    }
};
  1. 回到第2步,通过返回的size大小调用calloc申请内存空间,而系统最终给我们开辟的内存空间大小和size不一样。(详情看下一篇alloc如何开辟内存空间)

  2. 最后调用initInstanceIsa关联类。(详情看下下篇alloc如何关联类)

所以alloc其实已经返回了对象。

二. init

  1. alloc已经返回了对象,那init又做了什么:
- (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目的是为开发者提供工厂设计初始化方法,方便初始化设置。

三. new

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

new就是代替[[Class alloc]init]

你可能感兴趣的:(系统底层源码分析(11)——alloc、init、new)