在日常开发中,有的人会用[[Class alloc] init]
创建实例,也有的人会用[Class new]
的方式去创建实例。面试的时候,偶尔也会被问到这个问题。
那么,这两种方式创建出来的实例方法有什么区别呢?
本篇文章采用的源码是objc4-750.1版本
在Source/NSObject.mm文件中可以找到alloc
方法和new
方法的实现
+ (id)new {
return [callAlloc(self, false/*checkNil*/) init];
}
+ (id)alloc {
return _objc_rootAlloc(self);
}
// Replaced by ObjectAlloc
+ (id)allocWithZone:(struct _NSZone *)zone {
return _objc_rootAllocWithZone(self, (malloc_zone_t *)zone);
}
通过new
方法创建的实例会调用init
方法,那么[Class alloc] init]
和[Class new]
这两种方法的唯一区别就在于分配内存这一步,我们可以先找到_objc_rootAlloc
的实现函数
// Base class implementation of +alloc. cls is not nil.
// Calls [cls allocWithZone:nil].
id
_objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
等等,callAlloc
这个函数好熟悉啊,不就是new
方法里面调用的函数吗?唯一的区别就是new
方法调用的时候只传了2个参数,而_objc_rootAlloc
这里传了3个参数,难道这两个函数不一样?还是说多出来的第3个参数有默认参数
// Call [cls alloc] or [cls allocWithZone:nil], with appropriate
// shortcutting optimizations.
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];
}
从alloc
和new
进来这个函数的差别就是allocWithZone
参数了,从alloc
那里的时候为true,new
进来的时候为false
;整个函数用到allocWihZone
参数的地方就在于最后2行。
由于中间的东西涉及到OC中对象的本质内容,这里先不看,毕竟本文要探究的是alloc和new的区别,所以忽略,有兴趣的可以自行阅读,主要对自定义alloc
和allocWithZone
类的处理。
言归正传,如果allocWithZone为真则会走[cls allocWithZone:nil]
,反之则走[cls alloc]
。
// 2019.4.1修改
从上面我们可以得知,通过[Class alloc]
进入的最后会调[Class allocWithZone:nil]
方法;而通过[Class new]
方法最后则会调[Class alloc]
,然后再进入callAlloc
去调用[Class allocWithZone:nil]
从上面我们可以得知,如果没有重写allocWithZone:
方法,则[Class new]
分配内存的方式和[Class alloc]
分配方式是一样的,走fastpath(!cls->ISA()->hasCustomAWZ())
里面的;而如果重写了allocWithZone:
方法,则[Class new]
会走到[Class alloc]
方法
如果使用new的话,初始化方法就被固定只能调用
init
了,但如果用alloc init方式的话就不一定要用init了,还可以用initXXX等方法了。
总结
[Class new]就相当于调用[[Class alloc] init],一个为隐式调用,一个为显示调用而已。
打个小广告:Objective-C类和Objective-C中的对象这两篇文章都有简单讲到OC对象本质。
网友评论