OC底层01:alloc底层分析

开发中,我们经常使用到[[xxx alloc] init],但却没有真正理解过alloc,init,现在进行深入了解一下。

alloc,init的功能

1.先看看这样的一个例子
//创建一个Person类,然后引用对应头文件
        Person *p1 = [Person alloc];
        Person *p2 = [p1 init];
        Person *p3 = [p1 init];
        
        NSLog(@"%p-%p", p1, &p1);
        NSLog(@"%p-%p", p2, &p2);
        NSLog(@"%p-%p", p3, &p3);

运行结果如图:
运行结果
  • p1,p2,p3的指向地址都相同
  • p1,p2,p3的地址不同
2.alloc与init的关系如图
  • alloc生成对象地址。
  • init分配指针地址,并指向对象地址

alloc流程

需要看到alloc流程,可到苹果开源网站下载。
如果要将源码进行编译调试,可参见

对源码进行分析

从alloc方法一步一步点进去我们分别能看到:

//NSObject.mm文件中  +alloc方法
+ (id)alloc {
    return _objc_rootAlloc(self);
}

//NSObject.mm文件中  _objc_rootAlloc(Class cls)方法
id _objc_rootAlloc(Class cls)
{
    return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}

//NSObject.mm文件中  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));
}

//objc-runtime-new.mm文件中  _objc_rootAllocWithZone(Class cls, malloc_zone_t *zone __unused)方法 
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);
}

//objc-runtime-new.mm文件中 _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);
}

1. 前几步一条路进入可以不提,在callAlloc方法中,出现了分支slowpathfastpath,快速路径与慢速路径,可以理解为大多数情况走快速路径,极少情况走慢速路径,这样写便于运行效率的提升,所以我们继续研究_objc_rootAllocWithZone方法。
2. 紧接着我们到了关键的_class_createInstanceFromZone方法:
  1. _class_createInstanceFromZone第一个研究的方法size = cls->instanceSize(extraBytes);
    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;
        return size;
    }

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

经过调试,我们能看到进入fastInstanceSize方法,然后执行return align16(size + extra - FAST_CACHE_ALLOC_DELTA16);

static inline size_t align16(size_t x) {
    return (x + size_t(15)) & ~size_t(15);
}

很明显,这是在进行内存对齐(下一章节具体讲解)

3. 得到一个16字节倍数大小size,然后执行obj = (id)calloc(1, size);分配内存空间(ps:if (zone)现已废弃,不做过深研究)

当我们在执行obj = (id)calloc(1, size);后我们答应obj可以看到:


而我们一个对象的打印的格式应该为<Person: 0x10060de90>,所以calloc只是分配了一个地址空间,并没有创建新的指针指向它。也证明了前面alloc的说法

4.当我们执行完obj->initInstanceIsa(cls, hasCxxDtor);我们拿到我们想要的结果:

综上alloc运行流程如图:


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