[xxx alloc] init]
非常熟悉了,都知道是创建一个xxx的对象,但是OC底层到底做了什么?首先看下方代码:
HRTest * t = [HRTest alloc];
HRTest * tt = [t init];
HRTest * ttt = [t init];
HRTest * tttt = [HRTest alloc];
NSLog(@"%@---%p---%p",t,t,&t);
NSLog(@"%@---%p---%p",tt,tt,&tt);
NSLog(@"%@---%p---%p",ttt,ttt,&ttt);
NSLog(@"%@---%p---%p",tttt,tttt,&tttt);
常识:
高位到低位
; 堆区存放原则:从低位到高位
alloc
之后获得了不同的内存空间
,经过init
之后内存空间
相同。
推断:内存空间是由alloc负责申请,从这个角度看init并没处理任何动作 堆区
; 对象的指针存放在栈区
用一张图来解释:
想要一探alloc
是如何申请了内存空间的,就需要使用上篇中提到的objc源码了。废话不多说,打开源码,加上断点,一步步开始调试:
此处有两种可能,简述流程省略代码:
NSObjec
直接进入alloc
流程_objc_alloc
->callAlloc
->alloc
,为什么会进入_objc_alloc而不是调用的alloc这就要涉及到llvm
中的知识,后续有机会再来解释,可以简单理解为llvm做了一次类似于hook
的操作,将alloc
转为_objc_alloc
_class_createInstanceFromZone
,这个方法是正在来进行内存申请操作的地方alloc流程图
_class_createInstanceFromZone流程图
//此处只放出最核心代码
_class_createInstanceFromZone(...){
...
size_t size;
size = cls->instanceSize(extraBytes);
}
size_t instanceSize(size_t extraBytes) const {
//编译快速计算所占内存大小
if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
return cache.fastInstanceSize(extraBytes);
}
size_t size = alignedInstanceSize() + extraBytes;
if (size < 16) size = 16;
return size;
}
size_t fastInstanceSize(size_t extra) const
{...
//计算实际内存占用
size_t size = _flags & FAST_CACHE_ALLOC_MASK;
return align16(size + extra - FAST_CACHE_ALLOC_DELTA16);
}
static inline size_t align16(size_t x) {
//著名的字节对齐算法
return (x + size_t(15)) & ~size_t(15);
}
//此处只放出最核心代码
_class_createInstanceFromZone(...){
...
id obj;
if (zone) {
obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
} else {
// 申请1块size大小的内存空间
obj = (id)calloc(1, size);
}
}
zone
方式:在iOS8以后就基本不使用了calloc
返回的是一个id
,表示当前并无类型//此处只放出最核心代码
_class_createInstanceFromZone(...){
...
id obj;
if (!zone && fast) {
//一般会进入此判断
obj->initInstanceIsa(cls, hasCxxDtor);
} else {
obj->initIsa(cls);
}
}
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
...
//进行类型赋值
isa = isa_t((uintptr_t)cls);
}
看完全部流程是不是感觉到流程索然繁杂,但是本质并不复杂,狗头
在源码中反复出现的这两个宏定义,我觉得有必要简单解释一下:
//x很可能为真, fastpath 可以简称为 真值判断
#define fastpath(x) (__builtin_expect(bool(x), 1))
//x很可能为假,slowpath 可以简称为 假值判断
#define slowpath(x) (__builtin_expect(bool(x), 0))
1,自定义类第一次callAlloc时没有找到默认的allocWithZone,经过objc_msgsend(alloc)之后,第二次callAlloc时找到了默认的allocWithZone。allocWithZone是什么时候创建加载的呢?
- (id)init {
return _objc_rootInit(self);
}
id _objc_rootInit(id obj)
{
return obj;
}
自定义做一些处理提供一个入口
一般在开发中,初始化除了init,还会使用new
,通过源码来看两者本质上并没有什么区别
+ (id)new {
retur [callAlloc(self, false/*checkNil*/) init];
}
但是在一般的开发中,如果使用自定的类,这里并不建议使用new
,因为这里系统只会调用init方法,对于自定义的initWhitXXX
并不会调用。但是系统自己类大可放心使用.
initWhitCustom
并没有调用参考资料: