iOS 关于oc的alloc的那些事

679 阅读2分钟

关于alloc

alloc是oc对象实例化方法,想必大家在日常工作中alloc已经写了无数次,例如:

LGPerson *person1 = [LGPerson alloc] ;
LGPerson *person2 = [[LGPerson alloc] init];
LGPerson *person3 = [[LGPerson allocWithZone:nil] init];
LGPerson *person4 = [LGPerson new];

那么问题来了,这几种alloc有什么区别呢?

我们先说一下这几种分别做了什么,然后我们再来解析具体流程。 我们从下往上说:

  • new方法等同alloc和init,alloc分配了内存,然后调用init初始化,new在分配内存后直接调用init进行初始化,一步到位
  • 说起allocWithZone就要说一下NSZone,在苹果的官方文档中是这样介绍的:NSZone是apple用来说分配和释放内存的一种方式,它不是一个对象,而是使用c结构存储了关于对象的内存信息。allocWithZone方法是历史遗留问题,现在也不使用了,allocWithZone和alloc没有区别,等下在源码解析的也会看到
  • 最后说下person1和person2,这两种的区别是,person1分配了内存没有初始化,而person分配了内存同时也完成了初始化

不难发现,这几种写法最后都和alloc方法离不开,那么我们现在来探索一下alloc做了些什么?

源码准备

源码跟踪

b4fb0fc7ebab9f6434439b7d7ce6bfc.jpg

  • 我们可以直观的看到alloc内部调用了_objc_rootAlloc,allocWithZone内部调用了_objc_rootAllocWithZone,然后我们继续跟踪看下这两个函数内部的调用

8bf72d944b4267acaa5ad860cb275e8.jpg

  • 在这里就可以看到为什么说alloc和allocWithZone没有什么区别,因为它们内部都调用了callAlloc函数 那么我们再看一下callAlloc最终内部实现了什么

042bfcdcd2b596bbbd61814caebf7ae.jpg

  • 断点调试调用了_objc_rootAllocWithZone

09235d9744da9f0d931e52a9b8db803.jpg

  • 然后内部又调用了_class_createInstanceFromZone

00323c37ab7a6cfee76f9a6eb6af9d1.jpg

0ff49d3c469b4335a3ca1857f8ea45c.jpg

e8410285a1a2cb3a3968aeacf2bed21.jpg

0a222e0734a1e11b8cfec49249727c1.jpg

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;

    id obj;
    if (zone) {
        // 2;allocWithZone指定内存池
        obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
    } else {
        // 2;怎么去申请内存
        obj = (id)calloc(1, size);
    }
    if (slowpath(!obj)) {
        if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
            return _objc_callBadAllocHandler(cls);
        }
        return nil;
    }

    // 3: 将 cls类 与 obj指针(即isa) 关联
    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);
}
  • 现在可以看到在这个方法中计算所需内存大小,以及字节对齐,调用calloc分配内存,最后通过initInstanceIsa将cls和isa关联起来
  • 这样整个流程走完,alloc对象创建分配完成

总结

现在可以梳理一下整个流程 alloc->_objc_rootAlloc->callAlloc->_objc_rootAllocWithZone->_class_createInstanceFromZone->(instanceSize、calloc、initInstanceIsa)

f30ba8bd466f1d60e92bcdb82188182.png