写了多年的OC代码,alloc&init是老朋友了,我们总是在创建对象的时候总是用到,但却没有探究过其背后到底做了什么?这篇博客带你深入了解alloc&init。
1.对象alloc和init的后内存地址
可以看到:alloc会创建一块内存控件,init不会创建内存空间也不会改变内存空间地址,p1 p2 p3三个指针地址为连续的,指向了同一块内存空间。
2.alloc方法的执行流程
备注:当前运行在模拟器,真机cpu指令集不一样
1.下好断点,运行程序
2.运行程序到断点处,添加符号断点
请注意执行到此处断点时在添加符号断点,这是给所有类的alloc方法都加上了断点。
3.点击 Step over
4.点击会进入libobjc.A.dylib
这里可以看到这里调用了父类(NSObject)的方法 +[NSObject alloc] , 还可以看到父类的方法jmp到了_objc_rootAlloc
5.继续添加符号断点,追踪_objc_rootAlloc
6.点击 Step over,查看 _objc_rootAlloc
这里可以看到 _objc_rootAlloc 有调用到 _objc_rootAllocWithZone,继续添加符号断点,继续追查
7.继续添加符号断点,查看 _objc_rootAllocWithZone
运行断点进入到 ”_objc_rootAllocWithZone“ 可以看到在22有retq 在这里return了,回到_objc_rootAlloc,然后执行objc_msgSend
到此alloc的基本走的流程已经清楚了,alloc->_objc_rootAlloc->_objc_rootAllocWithZone->objc_msgSend,至于具体流程是如何,我们来查看源代码
3.alloc在源码中如何执行
苹果源码下载地址:
这里用的是objc4-818.2版本,具体的源码如何配置这里不做过多的说明。
1. 进入到alloc方法可以看到首先调用的就是上面我们在libobjc.A.dylib看到的
_objc_rootAlloc 方法
2. 然而进入_objc_rootAlloc看到的调用的是callAlloc,进入callAlloc里面才发现里面有调用_objc_rootAllocWithZone(很显然callAlloc没有变成汇编的jmp调用而是直接优化成汇编代码了),接下来看一下逻辑!cls->ISA()->hasCustomAWZ()
3. !cls->ISA()->hasCustomAWZ(),这里是去cache中查看是否有缓存了,如果有会直接执行_objc_rootAllocWithZone,如果没有会通过objc_msgSend调用alloc方法,然后又是_objc_rootAlloc->callAlloc,cache中存在缓存时就会调用_objc_rootAllocWithZone
4. 进入到_objc_rootAllocWithZone,里面调用到_class_createInstanceFromZone
5. 进入_class_createInstanceFromZone,发现alloc的具体操作都在这里,接下来就是具体分析_class_createInstanceFromZone进行了什么操作
_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;
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) {
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);
}
6. 首先是cls->instanceSize()计算需对象要开辟的内存空间,接下来是calloc()开辟内存空间,然后obj->initInstanceIsa()
7. 到这里感觉alloc的执行流程已经明了了,但是还有一个疑问,在符号断点时明明是执行的objc_alloc,objc_alloc到底有没有执行到,我们还要深究一下,我们给alloc、objc_alloc、callAlloc、_objc_rootAlloc、_objc_rootAllocWithZone、_class_createInstanceFromZone都机上一个printf打印,然后执行试一试。
这里发现NSObject执行流程objc_alloc->callAlloc->_objc_rootAllocWithZone->_class_createInstanceFromZone。我们自己定义的类的执行顺序为objc_alloc->callAlloc->alloc->objc_rootAlloc->callAlloc->_objc_rootAllocWithZone->_class_createInstanceFromZone
8. 总结 alloc流程
NSObject执行流程objc_alloc->callAlloc->_objc_rootAllocWithZone->_class_createInstanceFromZone。
我们自己定义的类的执行顺序为objc_alloc->callAlloc->alloc->objc_rootAlloc->callAlloc->_objc_rootAllocWithZone->_class_createInstanceFromZone