写在前面
作为一名多年写业务代码的iOS码农,离不开创建类创建对象,alloc init使用非常频繁。究竟alloc和init做了什么呢。带着疑问,一起来探索。
XHPerson *x0 = [XHPerson alloc];
XHPerson *x1 = [x0 init];
XHPerson *x2 = [x1 init];
NSLog(@"x0==%@--%p--%p", x0, x0, &x0);
NSLog(@"x1==%@--%p--%p", x1, x1, &x1);
NSLog(@"x2==%@--%p--%p", x2, x2, &x2);
创建了三个对象,分别打印他们的对象内容、对象地址、指针地址允许结果会如何,都一样吗?
x0==<XHPerson: 0x1013a2770>--0x1013a2770--0x7ffeefbff468
x1==<XHPerson: 0x1013a2770>--0x1013a2770--0x7ffeefbff470
x2==<XHPerson: 0x1013a2770>--0x1013a2770--0x7ffeefbff460
可以看到,三个对象的内容和地址是一样的,指针地址不一样的。
结论: 1.alloc有内存指针指向。 2.init所指内存是一样的,没有对指针进行操作。 3.alloc有开辟内存功能,init没有开辟内存功能
alloc源码
那么既然alloc做了开辟内存的事情,它是怎么做到的呢?难道init什么都不做吗,有什么用呢?继续往下走。 源码流程:
1.alloc
+ (id)alloc {
return _objc_rootAlloc(self);
}
2._objc_rootAlloc
id
_objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
3.callAlloc
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
#if __OBJC2__ //objc2.0版本
//编译器对代码进行优化
if (slowpath(checkNil && !cls)) return nil;
//判断该类是否实现自定义的 +allocWithZone,没有则进入if条件句
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));
}
4._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);
}
5._class_createInstanceFromZone(核心创建实例方法)
做了三个重要的事情,用截图看的更加清楚
对象创建流程
1.`cls->instanceSize` 计算内存大小。
2.`(id)calloc(1, size)` 开辟内存。
3.`obj->initInstanceIsa(cls, hasCxxDtor)` 将类和内存关联起来。
instanceSize方法
fastInstanceSize方法
align16方法,16字节内存对齐
无缓存alignedInstanceSize方法
calloc方法
calloc开辟内存并没有执行所定义的类
initInstanceIsa方法
初始化isa,isa底层原理暂时不做分析。
最后
alloc流程图
总结
通过对alloc源码的分析,可以得知alloc的主要目的就是开辟内存,关联isa和cls类。 其中开辟内存的核心步骤有三步:计算内存大小、申请内存空间、关联isa和cls类