我们知道alloc、init、new就是开辟内存空间初始化对象,今天就来探讨一下它们分别在底层干了什么。
一. alloc
首先简单创建项目,调用alloc
并断点:
然后进入汇编模式运行(Debug -> Debug Workflow -> Always Show Disassembly):
我们发现原来调用alloc
时,底层会调用objc_alloc
。也许下面代码可以说明:
(也可从LLVM源码探究调用alloc会走objc_alloc)
- 从objc4-750源码探究:
// Calls [cls alloc].
id
objc_alloc(Class cls)
{
return callAlloc(cls, true/*checkNil*/, false/*allocWithZone*/);
}
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())) { ... }//canAllocFast只返回false
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];//先走objc_alloc然后走到这,接着才真正调用alloc
}
+ (id)alloc {
return _objc_rootAlloc(self);
}
id
_objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);//传参不一样
}
- 这里再次调用
callAlloc
,然而这次会走class_createInstance
:
id
class_createInstance(Class cls, size_t extraBytes)
{
return _class_createInstanceFromZone(cls, extraBytes, nil);
}
static __attribute__((always_inline))
id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
bool cxxConstruct = true,
size_t *outAllocatedSize = nil)
{
...
size_t size = cls->instanceSize(extraBytes);
if (outAllocatedSize) *outAllocatedSize = size;
...
id obj;
if (!zone && fast) {
obj = (id)calloc(1, size);//开辟内存空间
if (!obj) return nil;
obj->initInstanceIsa(cls, hasCxxDtor);//关联指向类
}
else { ... }
...
return obj;
}
- 来到这里,通过
instanceSize
函数获取开辟内存空间大小:
struct objc_class : objc_object {
...
// May be unaligned depending on class's ivars.
uint32_t unalignedInstanceSize() {
assert(isRealized());
return data()->ro->instanceSize;//返回大小
}
// Class's ivar size rounded up to a pointer-size boundary.
uint32_t alignedInstanceSize() {
return word_align(unalignedInstanceSize());
}
size_t instanceSize(size_t extraBytes) {
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;//预留字节,保证安全
return size;
}
...
}
# define WORD_MASK 7UL
static inline uint32_t word_align(uint32_t x) {
return (x + WORD_MASK) & ~WORD_MASK;//内存对齐,变成8的倍数
}
最后是通过ro
返回大小,我们可以看一下ro
:
struct class_rw_t {
...
const class_ro_t *ro;
...
}
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name;
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
method_list_t *baseMethods() const {
return baseMethodList;
}
};
回到第2步,通过返回的
size
大小调用calloc
申请内存空间,而系统最终给我们开辟的内存空间大小和size
不一样。(详情看下一篇alloc如何开辟内存空间)最后调用
initInstanceIsa
关联类。(详情看下下篇alloc如何关联类)
所以
alloc
其实已经返回了对象。
二. init
-
alloc
已经返回了对象,那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
就是代替[[Class alloc]init]