前言:这个知识点大多都已经知晓,[[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没有任何区别。