详细分析 alloc 流程
0. 从main 入手-找一下感觉
在我们常见的app项目中,在main函数中打一个断点,并且再添加一个_objc_init
d的符号断点,方便看调用栈状态。
可以大概看出一个加载流程
image.png
1. 探索alloc
我们先就简单粗暴的来看看,新建一个TestNSObject 的类,然后 在alloc 处下一个断点,
截屏2020-02-29下午3.21.40.png
control + in (或者直接看汇编代码,或者符号断点)
最后可以看出,alloc
最后其实是libObjc
动态的objc_alloc
2. 探索libObjc
源码
通过断点分析,调用流程图如下:
NSObject-alloc流程2.png
配合核心源码分析
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];
}
宏的说明
fastpath(x)
表示x很可能不为0;slowpath(x)
表示x很可能为0。——这里表示cls大概率是有值的,编译器可以不用每次都读取 return nil 指令,都是希望编译器进行优化
第二次 callAlloc 参数说明:
checkNil
是否需要判空直接传的是 false ,站在系统角度,前面已经在第一次调用 callAlloc 的时候进行了判空了,没必要再次进行判空的了。第三个参数allocWithZone
传的是 true ,关于这个方法,我查阅了苹果开发者文档,文档解释如下:
Do not override allocWithZone: to include any initialization code. Instead, class-specific versions of init... methods.
This method exists for historical reasons; memory zones are no longer used by Objective-C.
译:不要去重载 allocWithZone 并在其内部填充任何初始化代码,相反的,应该在 init... 里面进行类的初始化操作。
这个方法的存在是有历史原因的,内存 zone 已经不再被 Objective-C 所使用的。
按照苹果开发者文档的说法,其实 allocWithZone 本质上和 alloc 是没有区别的,只是在 Objective-C 远古时代,程序员需要使用诸如 allocWithZone 来优化对象的内存结构,而在当下,其实你写 alloc 和 allocWithZone 在底层是一样的。
方法里面两个if的判断
-
if (fastpath(!cls->ISA()->hasCustomAWZ()))
bool hasCustomAWZ() { return ! bits.hasDefaultAWZ(); } bool hasDefaultAWZ() { return data()->flags & RW_HAS_DEFAULT_AWZ; } #define RW_HAS_DEFAULT_AWZ (1<<16) struct class_rw_t { // Be warned that Symbolication knows the layout of this structure. uint32_t flags; ...省略 } struct objc_class : objc_object { // Class ISA; Class superclass; cache_t cache; // formerly cache pointer and vtable class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags class_rw_t *data() { return bits.data(); } ...省略 }
这里实际上是判断cls 也就是object_class
这一结构体内部的class_rw_t
的flags
与上一个宏RW_HAS_DEFAULT_AWZ
的结果。
测试结论:在第一次进入 callAlloc 方法内部的时候, flags 值为 1 ,然后 flags 与上 1<<16 结果就是 0 ,所以第一次就会跳过 if 里面的逻辑;而第二次进入 callAlloc 方法内部的时候, flags 值是一个很大的整数,与上 1<<16 后结果并不为0 ,就进入 if 里面的逻辑了。
-
if (fastpath(cls->canAllocFast()))
bool canAllocFast() { assert(!isFuture()); return bits.canAllocFast(); } bool canAllocFast() { return false; }
可以看出一直返回false,所以就走到了核心 class_createInstance-> _class_createInstanceFromZone
核心创建实例 _class_createInstanceFromZone 分析
static __attribute__((always_inline))
id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
bool cxxConstruct = true,
size_t *outAllocatedSize = nil)
{
//一些常规的非空、以及容错处理
if (!cls) return nil;
assert(cls->isRealized());
// Read class's info bits all at once for performance
bool hasCxxCtor = cls->hasCxxCtor();
bool hasCxxDtor = cls->hasCxxDtor();
bool fast = cls->canAllocNonpointer();
//获取cls的实例内存大小,extraBytes为0
size_t size = cls->instanceSize(extraBytes);
//outAllocatedSize 默认nil
if (outAllocatedSize) *outAllocatedSize = size;
id obj;
//zone默认是nil,fast是true
if (!zone && fast) {
// 根据实例size开辟内存
obj = (id)calloc(1, size);
if (!obj) return nil;
//将cls和是否有c++析构器传入给initInstanceIsa,实例话isa
obj->initInstanceIsa(cls, hasCxxDtor);
}
else {
//只有 allocWithZone 或 copyWithZone 会来到下面的逻辑
if (zone) {
// 根据给定的 zone 和 size 开辟内存
obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);
} else {
// 根据 size 开辟内存
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);
}
//如果有 C++ 初始化构造器和析构器,优化加速整个流程
if (cxxConstruct && hasCxxCtor) {
obj = _objc_constructOrFree(obj, cls);
}
return obj;
}
结论:
alloc 开辟申请内存空间
伴随初始化isa