macOS Big Sur
11.0.1
Xcode 12.4
objc4-818.2
Apple Open Source
alloc调用底层源码:
+ (id)alloc {
return _objc_rootAlloc(self);
}
// 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*/);
}
// Call [cls alloc] or [cls allocWithZone:nil], with appropriate
// shortcutting optimizations.
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));
}
cls->ISA()->hasCustomAWZ()
fastpath中的cls->ISA()->hasCustomAWZ() 时判断一个类是否有自定义的 +allocWithZone 实现,如果没有自定义allocWithZone实现,则会执行if条件语句,即: _objc_rootAllocWithZone方法
_objc_rootAllocWithZone
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);
}
_class_createInstanceFromZone
这里是alloc源码的核心操作:
- cls->instanceSize() 计算需要开辟的内存大小
- calloc() 申请内存
- obj->initIsa() 将类与isa关联
/***********************************************************************
* class_createInstance
* fixme
* Locking: none
*
* Note: this function has been carefully written so that the fastpath
* takes no branch.
**********************************************************************/
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;
//计算需要开辟的内存大小 根据上一步调用可知道 传入的extraBytes为0
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) {
//将cls类与obj指针(isa)关联
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);
}
init
- (id)init {
return _objc_rootInit(self);
}
id
_objc_rootInit(id obj)
{
// In practice, it will be hard to rely on this function.
// Many classes do not properly chain -init calls.
return obj;
}
发现init方法实际就是返回了当前对象,这是一种抽象工厂设计模式的体现。
new
+ (id)new {
return [callAlloc(self, false/*checkNil*/) init];
}
可以看到new类方法,同样调用了callAlloc方法,只是相比alloc调用callAlloc少了一个参数(allowWithZone),并且new类方法默认包装了init实例方法。
.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject
@end
NS_ASSUME_NONNULL_END
.m
#import "Person.h"
@implementation Person
//+ (instancetype)allocWithZone:(struct _NSZone *)zone {
// static id instance = nil;
// @synchronized (self) {
// if (instance == nil) {
// instance = [super allocWithZone:zone];
// }
// }
// return instance;
//}
@end
新建一个Person类,以下针对4种情况进行测试:
-
Person未重写allocWithZone: & 通过alloc init方式创建实例对象
- 先跳入callAlloc方法,执行最后一句 即:return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc));
- 调用alloc类方法 即
+ (id)alloc { return _objc_rootAlloc(self); }
- 接着上一步 执行_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方法中的if判断
if (fastpath(!cls->ISA()->hasCustomAWZ())) { return _objc_rootAllocWithZone(cls, nil); }
-
Person重写了allocWithZone: & 通过alloc init方式创建实例对象
- 前三步与上面情况一致,但因为重写了allocWithZone:方法,就不会执行if (fastpath(!cls->ISA()->hasCustomAWZ()))条件语句,而是会继续往下走,由于alloc调用callAlloc(cls, false/checkNil/, true/allocWithZone/);最后一个参数为true,则会执行下面语句:
if (allocWithZone) { return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil); }
-
Person未重写allocWithZone: & 通过new方式创建实例对象
- 调用new类方法
+ (id)new { return [callAlloc(self, false/*checkNil*/) init]; }
- 同Person *person = [[Person alloc] init]; 的调用顺序一样,也进入了下面的if判断中
if (fastpath(!cls->ISA()->hasCustomAWZ())) { return _objc_rootAllocWithZone(cls, nil); }
-
Person重写了allocWithZone: & 通过new方式创建实例对象
- 调用new类方法
- 由于new类方法 底层[callAlloc(self, false/checkNil/) init]; allocWithZone布尔值未传,则不会执行fastpath(!cls->ISA()->hasCustomAWZ())语句中代码也不会执行if (allocWithZone),会直接执行return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc)); 实际又调用了alloc,由于重写了allocWithZone: 又会调用下面方法:
if (allocWithZone) { return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil); }
综上: [Class new] 就相当于[[Class alloc] init],前者是隐式调用,后者为显式调用。
如果使用new的话,初始化方法就被固定只能调用init了,但如果用alloc init方式的话就不一定要用init了,还可以用initXXX等方法了。
参考:
iOS底层探究-alloc/init做了什么?
Object-C alloc 底层实现原理
iOS 中的 alloc init和new的区别
网友评论