OC 对象 alloc 流程分析

658 阅读2分钟

前期准备

下载 objc 源码,创建可断点调试工程,参考下面的文章
github.com/LGCooci/obj…

alloc流程图

验证过程

我们在 alloc 前打上断点,然后顺着 alloc 一路点进去

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


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


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

callAlloc 函数,我们打上三个断点,便于调试。点击 continue,进入下一个断点,如图。

我们可以看到,在调用 callAlloc 之前,还调用了一个函数

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

继续 continue,又进到了callAlloc 函数,但是这次进入到了 _objc_rootAllocWithZone,继续 _class_createInstancesFromZone

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


unsigned
_class_createInstancesFromZone(Class cls, size_t extraBytes, void *zone, 
                               id *results, unsigned num_requested)
{
    unsigned num_allocated;
    if (!cls) return 0;

    size_t size = cls->instanceSize(extraBytes);

    num_allocated = 
        malloc_zone_batch_malloc((malloc_zone_t *)(zone ? zone : malloc_default_zone()), 
                                 size, (void**)results, num_requested);
    for (unsigned i = 0; i < num_allocated; i++) {
        bzero(results[i], size);
    }

    // Construct each object, and delete any that fail construction.

    unsigned shift = 0;
    bool ctor = cls->hasCxxCtor();
    for (unsigned i = 0; i < num_allocated; i++) {
        id obj = results[i];
        obj->initIsa(cls);    // fixme allow nonpointer
        if (ctor) {
            obj = object_cxxConstructFromClass(obj, cls,
                                               OBJECT_CONSTRUCT_FREE_ONFAILURE);
        }
        if (obj) {
            results[i-shift] = obj;
        } else {
            shift++;
        }
    }

    return num_allocated - shift;    
}

_class_createInstancesFromZone 函数有三个重点函数
instanceSize 获取当前类所需字节数,进行 16 字节对齐 calloc 为当前类开辟的内存 initInstanceIsa 将当前类的信息和开辟的内存进行关联

以下为debug证实

此时内存开辟了,但是 obj 还是 id 类型,没有当前类的信息。继续下一步

initInstanceIsa 函数将当前类信息与开辟的内存进行关联,已经是我们要创建的类了。

NSObject 特性

NSObject *objc  = [NSObject alloc];

第一次执行到 callAlloc,满足条件,直接调用了 _objc_rootAllocWithZone, 没有调用 +alloc

判断条件源码如下:

inline Class 
objc_object::ISA() 
{
    ASSERT(!isTaggedPointer()); 
#if SUPPORT_INDEXED_ISA
    if (isa.nonpointer) {
        uintptr_t slot = isa.indexcls;
        return classForIndex((unsigned)slot);
    }
    return (Class)isa.bits;
#else
    return (Class)(isa.bits & ISA_MASK);
#endif
}

bool hasCustomAWZ() const {
    return !cache.getBit(FAST_CACHE_HAS_DEFAULT_AWZ);
}

// class or superclass has default alloc/allocWithZone: implementation
// Note this is is stored in the metaclass.
#define FAST_CACHE_HAS_DEFAULT_AWZ    (1<<14)

执行流程如下图