iOSiOS开发知识小集iOS

iOS 中 alloc init 和new的区别

2019-04-06  本文已影响33人  好雨知时节浩宇

前言:这个知识点大多都已经知晓,[[xx alloc] init] 跟 [xx new]是等价的。但是具体是如何等价的或许大多数人都解释不清楚,知识单纯的知道结论。这篇博文从源码角度来解释下为什么说二者是等价的。

一 、分别看下alloc init 源码实现:

1、alloc源码实现如下:

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

内部调用了 _objc_rootAlloc()函数,继续看下_objc_rootAlloc实现源码如下:

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

可以看到内部又调用了:callAlloc函数。继续看callAlloc的实现如下:(这里我们留意一下调用callAlloc函数时传的参数值!

static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
    if (slowpath(checkNil && !cls)) return nil;

#if __OBJC2__
    if (fastpath(!cls->ISA()->hasCustomAWZ())) {
        // No alloc/allocWithZone implementation. Go straight to the allocator.
        // fixme store hasCustomAWZ in the non-meta class and 
        // add it to canAllocFast's summary
        if (fastpath(cls->canAllocFast())) {
            // No ctors, raw isa, etc. Go straight to the metal.
            bool dtor = cls->hasCxxDtor();
            id obj = (id)calloc(1, cls->bits.fastInstanceSize());
            if (slowpath(!obj)) return callBadAllocHandler(cls);
            obj->initInstanceIsa(cls, dtor);
            return obj;
        }
        else {
            // Has ctor or raw isa or something. Use the slower path.
            id obj = class_createInstance(cls, 0);
            if (slowpath(!obj)) return callBadAllocHandler(cls);
            return obj;
        }
    }
#endif

    // No shortcuts available.
    if (allocWithZone) return [cls allocWithZone:nil];
    return [cls alloc];
}

我们简单看一下该函数的实现:

1、上面做了对象判空操作,之后做了预编译的处理,并根据cls->ISA()->hasCustomAWZ()判断是否实现allocWithZone函数,如果类自己实现了则不会执行if下面的逻辑。(!!!:重写allocWithZone的情况下改代码不会执行。)
2、再根据canAllocFast判断是否支持快速创建。

3、执行完上面预编译宏内的逻辑后,来到关键的地方,也就是

// No shortcuts available.
    if (allocWithZone) return [cls allocWithZone:nil];
    return [cls alloc];

这里我们可以清晰的看到,如果allocWithZone为true,则会执行allocWithZon函数,反之 直接执行alloc函数。

我们得出作出第一个总结:
结论1:在重写了allocWithZone的前提下,调用alloc函数时,在callAlloc函数内部最终会执行[cls allocWithZone:nil];

二、上面是alloc的源码实现,我们再看下new的源码实现:

+ (id)new {
    return [callAlloc(self, false/*checkNil*/) init];
}

我们可以看到new函数中直接调用了callAlloc函数(也就是上面我们分析的函数),而且它调用callAlloc函数时传递的参数中,没有传递allocWithZone的值,根据上面的函数实现,如果不传值会取默认值,即:allocWithZone = false。所以:
结论2:在重写了allocWithZone的前提下,调用new函数,在callAlloc函数内部最终会执行[cls alloc],然后再走一遍上面分析的alloc的逻辑,最终执行 [cls allocWithZone:nil]

三、结论

经过上面的对比分析,我们可以得出结论:

在重写了allocWithZone函数后,alloc init 和 new的区别只是在于:是否显示的调用allocWithZone函数。使用new时会在callAlloc中调用alloc,从而隐式调用allocWithZone:函数。

注:如果没有重写allocWithZone:函数,使用alloc init同new没有任何区别。

上一篇 下一篇

猜你喜欢

热点阅读