alloc
我们往里一直深究应该是以下的一个流程:
1.进入alloc
// 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*/);
}
// Calls [cls alloc].
id
objc_alloc(Class cls)
{
return callAlloc(cls, true/*checkNil*/, false/*allocWithZone*/);
}
// Calls [cls allocWithZone:nil].
id
objc_allocWithZone(Class cls)
{
return callAlloc(cls, true/*checkNil*/, true/*allocWithZone*/);
}
callAlloc中有三个参数:cls 类的信息 (Class)
/ checkNil 是否需要检查cls为不为nil
/ allocWithZone 如果为true,会默认在NSZone自动分配内存
2.进入callAlloc
// 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];
}
在这个方法中,先判断hasCustomAWZ()
,当前class是否有默认的allocWithZone,没有重写就会越过canAllocFast()
(当前默认是false)条件,并且直接调用class_createInstance()
与_class_createInstanceFromZone()
方法来开辟内存空间。
3.需要开辟的空间大小
在_class_createInstanceFromZone
方法中可以看到instanceSize
方法,在该方法中可以看到对象开辟的空间大小最少是16字节。
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;
}
而在alignedInstanceSize
方法中,我们可以看到,开辟空间大小的一个原则:内存对齐。
// Class's ivar size rounded up to a pointer-size boundary.
uint32_t alignedInstanceSize() {
//内存对齐
return word_align(unalignedInstanceSize());
}
static inline uint32_t word_align(uint32_t x) {
// 7+8 = 15
// 0000 1111
// 0000 1000
//&
// 1111 1000 ~7
// 0000 1000 8
// 0000 0111
//
// x + 7
// 8
// 8 二阶
// (x + 7) >> 3 << 3
return (x + WORD_MASK) & ~WORD_MASK;
}
static inline size_t word_align(size_t x) {
return (x + WORD_MASK) & ~WORD_MASK;
}
一个对象、指针 占用 8字节, 而我们对象所需的内存空间也得是8的倍数,默认规定内存空间必须是大于等于16字节
在计算需要开辟的内存后,我们需要申请内存、创建对象,初始化isa,并关联类、对象,在_class_createInstanceFromZone
方法中,如下:
id obj;
if (!zone && fast) {
//申请内存,创建对象
obj = (id)calloc(1, size);
if (!obj) return nil;
//初始化isa,将内存空间与类、对象关联起来
obj->initInstanceIsa(cls, hasCxxDtor);
}
else {
// 申请内存空间,创建对象
if (zone) {
obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);
} else {
obj = (id)calloc(1, size);
}
if (!obj) return nil;
// Use raw pointer isa on the assumption that they might be
// doing something weird with the zone or RR.
//初始化isa,将内存空间与类、对象关联起来
obj->initIsa(cls);
}
init
我们可以看看它的实现:
// Replaced by CF (throws an NSException)
+ (id)init {
return (id)self;
}
- (id)init {
return _objc_rootInit(self);
}
可以看出init返回的也是本身对象,它是工厂模式的一种表现形式,方便子类的重写与扩展。
new
+ (id)new {
return [callAlloc(self, false/*checkNil*/) init];
}
可以看出,就是alloc 和 init的结合。
最后,符号断点的三种调试技巧:
-
在对应的断点地方打上断点,然后按住Ctrl键,同时点击下图中圈出的部分,就会进入汇编页面
2.在对应的代码部分断点,然后用指定的断点搜索来查找对应的汇编部分,如下图:
3.在对应的代码部分断点,然后等运行卡在断点部分,选择xcode顶部菜单栏中 Debug-->DebugWorkflow-->Always Show Disassembly,如下图: