1.0 oc对象的创建
先来思考几个问题:1、alloc、init、new,初始化对象是不是都会分配内存?2、alloc int new 有什么区别?内存是否一样?3、一个对象最少占用多少内存?4、对象创建的流程是怎样的?
做个demo测试一下:
LGPerson *p1 = [LGPerson alloc];
LGPerson *p2 = [LGPerson new];
LGPerson *p3 = [p1 init];
LGPerson *p4 = [p1 init];
NSLog(@"%@-%p-%p-size:%ld",p1,p1,&p1,sizeof(p1));
NSLog(@"%@-%p-%p-size:%ld",p2,p2,&p2,sizeof(p2));
NSLog(@"%@-%p-%p-size:%ld",p3,p3,&p3,sizeof(p3));
NSLog(@"%@-%p-%p-size:%ld",p4,p4,&p4,sizeof(p4));
复制代码
输出结果如下:
<LGPerson: 0x283a848e0>-0x283a848e0-0x16fc11af8-size:8
<LGPerson: 0x283a848c0>-0x283a848c0-0x16fc11af0-size:8
<LGPerson: 0x283a848e0>-0x283a848e0-0x16fc11ae8-size:8
<LGPerson: 0x283a848e0>-0x283a848e0-0x16fc11ae0-size:8
复制代码
上面可以看出:1、alloc和new 都会初始化对象并分配内存空间,指向alloc所创建对象的指针的首地址是0x281abc3b0,指向new所创建对象的指针的首地址是0x281abc3c0。2、alloc和init的指针指向同一块内存区域都是从0x281abc3b0开始 3、指针是存放地址的一个变量
,0x16ee95af8、0x16ee95af0、0x16ee95ae8、0x16ee95ae0是4个指针变量的地址
总结:alloc
和new
创建对象都会分配内存空间,alloc/int指向同一块内存空间,new开辟了新内存空间。
那么alloc和new到底如何创建对象并分配内存的呢?
简单粗暴一点直接上源码断点调试,源码地址:objc4,在此感谢Cooci老师提供的可调式源码~~
2.0 alloc执行过程
先从几段重要的源码开始分析
2.0.1 callAlloc
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
#if __OBJC2__
if (slowpath(checkNil && !cls)) return nil;
// 如果有自定义的allocWithZone(hasCustomAWZ) 大概率走这个方法
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
return _objc_rootAllocWithZone(cls, nil);
}
#endif
//allocWithZone==true
if (allocWithZone) {
return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil);
}
return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc));
}
复制代码
分析:根据callAlloc
参数判断出会进入_objc_rootAllocWithZone
方法
2.0.2 _class_createInstanceFromZone
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;
//在iOS中,字节是8自己对齐,而内存是16字节对齐,所以小于16字节会补齐
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) {
//关联对象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);
}
复制代码
分析:先计算出对象所需内存空间,内存开辟都是16个字节进行对齐的,然后calloc开辟内存,最后关联对象isa。
2.0.3 instanceSize
inline size_t instanceSize(size_t extraBytes) const {
if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
return cache.fastInstanceSize(extraBytes);
}
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
}
复制代码
分析:word_align,align16
,这两个计算方式决定了ios中内存按16个字节对齐,字节按8字节对齐。也就是说创建一个对象至少需要开辟16个字节的内存空间
2.0.3 流程图分析
该图展示了alloc创建对象的执行流程。
2.0.4 calloc两次执行
在断点调试的时候,发现calloc执行了两次?通过调试与参阅资料发现系统内部会通过llvm
的函数方法把alloc
指向到objc_alloc
,在objc_alloc中又调用了callAlloc(cls, true, false)
,最后objc_msgSend转发alloc
,执行alloc方法。
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
#if __OB
//通过参数判断不执行此处代码
if (slowpath(checkNil && !cls)) return nil;
//通过参数判断不执行此处代码
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
return _objc_rootAllocWithZone(cls, nil);
}
#endif
// 传入的参数为false 此处不执行
if (allocWithZone) {
return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil);
}
//执行objc_msgsend 转发alloc
return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc));
}
复制代码
2.0.5 alloc详细流程补充
通过2.0.4我们知道了llvm系统内部会优先执行一次alloc方法,分析源码补充一张allo详细执行流程图。
3.0 new与init执行过程
+ (id)new {
return [callAlloc(self, false/*checkNil*/) init];
}
复制代码
我们看到new的实现就是执行了,callAlloc()
,然后执行了init操作。等价于[[XX alloc] init]
。
4.0 总结
1、alloc、new,都会初始化对象并分配内存?
2、alloc+int等价于new
3、一个对象最少占用16个字节内存