iOS底层原理alloc之初探

437 阅读2分钟

在我们的iOS开发中,经常会通过alloc去创建对象,调用alloc方法究竟做了什么?通过定位点击alloc方法,会进入到NSObject的alloc方法,但仅此而已,并不知道到实际做了什么?接下来我们通过3种方式窥探一下

一、3种方式

1、符号断点

<1>首先我们在ATPerson调用alloc的地方打个断点,运行代码,当断在alloc调用时,此时按住control键,点击Step into,可以看到调用objc_alloc了函数

001.png

1符号断点.png

<2>添加一个符号断点,选择Symbolic Breakpoint,输入objc_alloc,再次运行,可以看到调用了底层的libobjc.A.dylib`objc_alloc

2符号断点.png

2、通过汇编的方式

还是在alloc的位置打断点,运行,选择Debug-Debug Workflow-Always Show Disassembly

003汇编.png 此时可以看到以汇编的模式看到调用堆栈信息,也可以看到调用objc_alloc

004汇编.png

3、直接设置alloc符号断点

在[ATPerson alloc]加上断点,然后添加alloc符号断点,选择Symbolic Breakpoint,输入alloc, 添加完成先取消,然后运行,在走到[ATPerson alloc]时,打开alloc符号断点,此时可以看到调用底层的libobjc.A.dylib`+[NSObject alloc]:

005断点.png

通过以上3种方式可以知道alloc具体调用底层的什么方法

二、通过源码分析

首先可以打开opensource的官网下载对应的源码,本例以objc4-818.2版本说明

流程图

alloc流程图.png

通过alloc方法定位到

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

然后进入_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*/);
}

走到callAlloc

static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
#if __OBJC2__
    // slowpath: 慢速通道
    // fastpath: 快速通道
    // 根据编译器优化走哪个通道
    if (slowpath(checkNil && !cls)) return nil;
    // 是否有自定义的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));
}

重点代码

/***********************************************************************
* 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、计算内存大小
    size = cls->instanceSize(extraBytes);
    if (outAllocatedSize) *outAllocatedSize = size;

    // 2、开辟内存
    id obj;
    if (zone) {
        obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
    } else {
        obj = (id)calloc(1, size);
    }
    if (slowpath(!obj)) {
        if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
            return _objc_callBadAllocHandler(cls);
        }
        return nil;
    }

    // 3、绑定对象
    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);
}

根据上面3点拆开分析

1、计算内存大小
inline size_t instanceSize(size_t extraBytes) const {
        if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
            return cache.fastInstanceSize(extraBytes);
        }

        size_t size = alignedInstanceSize() + extraBytes;
        // CF requires all objects be at least 16 bytes.
        if (size < 16) size = 16;  // 最低返回16字节
        return size;
    }

进入cache.fastInstanceSize

size_t fastInstanceSize(size_t extra) const
    {
        ASSERT(hasFastInstanceSize(extra));

        if (__builtin_constant_p(extra) && extra == 0) {
            return _flags & FAST_CACHE_ALLOC_MASK16;
        } else {
            size_t size = _flags & FAST_CACHE_ALLOC_MASK;
            // remove the FAST_CACHE_ALLOC_DELTA16 that was added
            // by setFastInstanceSize
            return align16(size + extra - FAST_CACHE_ALLOC_DELTA16);
        }
    }

align16方法以16字节对齐

static inline size_t align16(size_t x) {
    return (x + size_t(15)) & ~size_t(15);
}
2、开辟内存

通过断点定位给obj分配内存空间,定义的id obj,在没有开辟内存空间之前查看也显示内存地址,此时是分配的脏内存(0x1002f6fc5)

110.png 继续往下走,根据之前计算的内存大小,执行calloc函数给对象开辟内存空间,此时obj赋有新的内存地址(0x100776020)

111.png

3、绑定对象

根据上面断点可以看到虽然给obj开辟了内存空间,但此时obj显示还是id类型,而没有关联到我们创建的对象上,接着可以看下面对象绑定的代码

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

我们继续断点查看,调用obj->initInstanceIsa后,此时可以看到obj已经关联了我们创建的对象ATPerson

112.png

以上就是对alloc的底层分析初探