iOS底层原理之NSObject的alloc源码分析

一、前言

作为一名白发且秃顶的iOS程序猿,几乎每天都是在创建对象(程序猿不担心找不到对象,想要对象alloc或是new一个就可以了),一行代码解决了,但是alloc底层究竟做了什么,今天大家就跟随本猿初探下alloc底层流程吧。

二、准备工作

1.下载源码objc4,下载最新版即可。

2.编译源码(开源库下载下来基本各种编译失败,需要自行修改相关配置,由于不在本篇范畴,这里略过)

三、3种底层探索分析方式

1.断点调用alloc方法
在需要调试的alloc地方断点,当代码执行到断点处,按住control + step into(单步)进入即可进入到汇编代码部分,如图:

image
image

2.符号断点
从上一步断点alloc进入得知之后进入objc_alloc方法,把objc_alloc添加符号断点如下:

image
image
image

3.汇编方式
当断点停留在alloc时,选择Xcode -> Debug -> Debug Workflow -> Always Show Disassembly进入汇编找到objc_alloc方法,操作如下图:

image
image

四、源码调试探索

根据源码编译工程,断点进入alloc调试,流程如下:
1.首先会进入objc_alloc方法

// Calls [cls alloc].
id
objc_alloc(Class cls)
{
    return callAlloc(cls, true/*checkNil*/, false/*allocWithZone*/);
}

2.接着第一次进入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));
}

3.接着通过objc_msgSend进入到alloc方法

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

4.进入_objc_rootAlloc方法

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

5.第二次进入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));
}

6.进入_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);
}

7.进入_class_createInstanceFromZone方法

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

8.进入alloc核心三步1-instanceSize,计算开辟内存空间所需的大小

inline void 
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
    ASSERT(!cls->instancesRequireRawIsa());
    ASSERT(hasCxxDtor == cls->hasCxxDtor());
    initIsa(cls, true, hasCxxDtor);
}

9.进入alloc核心三步2-calloc,开辟内存,返回地址指针

10.进入alloc核心三步3-initInstanceIsa,初始化指针,和类关联起来

inline void 
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
    ASSERT(!cls->instancesRequireRawIsa());
    ASSERT(hasCxxDtor == cls->hasCxxDtor());
    initIsa(cls, true, hasCxxDtor);
}

附上调用流程图如下:


自定义类alloc源码解析.png

五、总结

alloc 主要作用就是向系统申请开辟内存,通过isa指针与类进行关联。

著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处,谢谢。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容