OC底层之alloc探索

244 阅读2分钟

OC底层探索方式

在探索alloc底层实现之前,我们要掌握OC底层探索的三种方式:

一、通过下符号断点的方式确定相关底层函数的调用

以探索alloc为例:

1、在对象alloc这一行打一个断点,运行程序,来到断点位置

image.png

2、按住ctrl键,单击step info,我们可以直接来到objc_alloc方法

image.png

image.png

3、添加objc_alloc符号断点,我们就可以看到objc_alloc的实现方法了

image.png

二、通过汇编的方式(常用、推荐)

重复上面1步骤,我们来到断点后,点击Debug -- Debug Workflow -- Always Show Disassembly,就可以直接来到汇编代码,并确定当前底层执行的symbol(方法/函数)。再添加符号断点就可以看到objc_alloc的实现

image.png

三、通过已知的符号断点,依次确认未知的符号

我们可以直接下一个"alloc"符号断点,就可以来到libobjc.A.dylib`+[NSObject alloc]

image.png

通过objc源码探索alloc的底层实现

objc源码地址可以在opensource上找到并下载 打开objc源码,我们直接搜索alloc,可以看到:

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

+ (id)allocWithZone:(struct _NSZone *)zone {
    return _objc_rootAllocWithZone(self, (malloc_zone_t *)zone);
}

接下来我们直接通过断点调试,来一步一步跟踪alloc的流程

image.png

第一步,进入到alloc方法,之后执行_objc_rootAlloc

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

接下来,来到了callAlloc方法

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

这个方法里面,先有个nil的判断(通过参数直接判断),接下来会通过fastpath(!cls->ISA()->hasCustomAWZ())方法判断当前对象是否有自定义的allocWithZone方法,如果没有直接走_objc_rootAllocWithZone方法,有的话就通过objc_msgSend执行对象的alloc方法。通过调试,发现直接走到了_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);
}

直接来到_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);
}

主要有三个关键方法 cls->instanceSize这个方法是计算对象所需要的内存大小(这里面有个16字节对齐处理) cls->calloc这个方法主要是为对象申请开辟内存空间 cls->initInstanceIsa这个方法的主要作用是通过isa,将obj与cls进行关联 通过这些流程之后,我们的obj就被alloc出来了。

备注:这里面还有个分支流程需要后续补充,即:callAlloc方法中,cls若实现了allocWithZone方法的流程。