前言
探索下alloc
的方法执行流程
debug来调试runtime源码,执行以下逻辑:
NSObject *objc1 = [[NSObject alloc] init];
id __weak objc2 = objc1;
id __weak objc3 = objc2;
流程
这里为什么单独提一下alloc的流程,[NSObject alloc] 按照msgSend消息发送来解释,即给 NSObject 发送一个alloc消息,其实alloc方法在底层逻辑实现时,是会先走objc_alloc
然后再进行调用alloc方法。
objc_alloc
objc_alloc
调用 callAlloc
// Calls [cls alloc].
id
objc_alloc(Class cls)
{
return callAlloc(cls, true/*checkNil*/, false/*allocWithZone*/);
}
callAlloc
实现
// Call [cls alloc] or [cls allocWithZone:nil], with appropriate
// shortcutting optimizations.
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 slowpath
其中fastpath
和slowpath
就是告诉编译器,bool(x)
最可能的结果为1
或为0
,这样寄存器
读取指令时就默认优先
读取if_else
中的YES代码
部分或者NO代码
部分,达到优化目的。
#define fastpath(x) (__builtin_expect(bool(x), 1))
#define slowpath(x) (__builtin_expect(bool(x), 0))
就是likely
和unlikely
的实现,可以看 这篇博文
cls->ISA()->hasCustomAWZ()
hasCustomAWZ
方法意思就是判断是否实现自定义的allocWithZone
方法,如果没有实现就调用系统默认的allocWithZone
方法。
Tips:这里需要注意的是,
cls->ISA
有没有想过类的isa
初始化是什么时候,实例的isa
初始化是在alloc的时候,类的isa
的初始化是在load_images
方法执行时的
hasCxxCtor
和hasCxxDtor
表示是否有c++
类的创建和析构方法
由于 cls->canAllocFast()
源码写死固定返回false
,所以固定会走else中的语句
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;
}
所以到这为止,alloc执行步骤如下:
- 执行 objc_alloc
- 执行 callAlloc(cls,true,false)
- 判断是否实现自定义的allocWithZone方法,实现了就直接转到
5
,没有实现就执行 alloc 方法,转到4
- 执行 callAlloc(cls,false,true) ,并执行系统默认的allocWithZone方法
- 执行 class_createInstance
- 执行 class_createInstanceFromZone
allocWithZone对流程的影响
1.类没有实现allocWithZone
方法的时候是需要运行fastpath(!cls->ISA()->hasCustomAWZ())
判断里面的代码,最终是通过class_createInstance
的返回
id obj = class_createInstance(cls, 0);
if (slowpath(!obj)) return callBadAllocHandler(cls);
return obj;
class_createInstance
中实现如下
id class_createInstance(Class cls, size_t extraBytes)
{
return _class_createInstanceFromZone(cls, extraBytes, nil);
}
所以这个的流程是:alloc->_objc_rootAlloc->callAlloc(cls,true,false)->class_createInstance->_class_createInstanceFromZone
2.类实现了allocWithZone
方法的时候就不会运行fastpath(!cls->ISA()->hasCustomAWZ())
判断里面的代码,直接跑
if (allocWithZone) return [cls allocWithZone:nil];
并且这时候会执行类中实现的allocWithZone
方法
2020-04-28 23:55:18.173680+0800 LGTest[2370:69449] +[TestObject allocWithZone:]
通过断点的形式得到的流程是:
alloc->_objc_rootAlloc->callAlloc(cls,false,true)->allocWithZone->_objc_rootAllocWithZone->class_createInstance->_class_createInstanceFromZone
最终这两种情况下都会执行到_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
//判断当前class或者superclass是否有.cxx_construct构造方法的实现
bool hasCxxCtor = cls->hasCxxCtor();
//判断当前class或者superclass是否有.cxx——destruct析构方法的实现
bool hasCxxDtor = cls->hasCxxDtor();
bool fast = cls->canAllocNonpointer();
//通过进行内存对齐得到实例大小
size_t size = cls->instanceSize(extraBytes);
if (outAllocatedSize) *outAllocatedSize = size;
id obj;
if (!zone && fast) {
obj = (id)calloc(1, size);
if (!obj) return nil;
//初始化实例的isa指针
obj->initInstanceIsa(cls, hasCxxDtor);
}
else {
if (zone) {
obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);
} else {
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.
obj->initIsa(cls);
}
if (cxxConstruct && hasCxxCtor) {
obj = _objc_constructOrFree(obj, cls);
}
return obj;
}
下面是对_class_createInstanceFromZone
的分析
1.cls->instanceSize(extraBytes)
是进行内存对齐得到的实例大小,里面的流程分别如下:
size_t instanceSize(size_t extraBytes) {
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
}
uint32_t alignedInstanceSize() {
return word_align(unalignedInstanceSize());
}
uint32_t unalignedInstanceSize() {
assert(isRealized());
return data()->ro->instanceSize;
}
static inline uint32_t word_align(uint32_t x) {
return (x + WORD_MASK) & ~WORD_MASK;
}
#ifdef __LP64__
# define WORD_MASK 7UL
#else
# define WORD_MASK 3UL
#endif
一开始进来的时候extraBytes
为0的,因为当前的TestObject里面是没有属性的,是不是觉得开辟的内存空间是0呢?并不是的,因为还有一个isa
指针,指针在64位架构下为8字节,通过字节对齐不小于16字节,所以最终得到的是16字节。
通过流程可以知道在unalignedInstanceSize
方法中得到编译进去的实例的大小,因为isa
是一个对象指针,所以这个的大小是一个8
字节。所以传到word_align
方法里面的x为8
。其中WORD_MASK
在64位系统下是7,否则是3,因此,word_align()
方法在64位系统下进行计算是8
字节对齐按照里面的算法就是相当于8
的倍数。返回到instanceSize()
方法中的size就是对象需要的空间大小为8
,因为里面有小于16
的返回16
。
2.calloc
函数是初始化所分配的内存空间的。
3.initInstanceIsa
函数是初始化isa
,关联cls
的
init和new
1.介绍完alloc
之后,init
方法通过源码
- (id)init {
return _objc_rootInit(self);
}
id
_objc_rootInit(id obj)
{
// In practice, it will be hard to rely on this function.
// Many classes do not properly chain -init calls.
return obj;
}
其实就是返回它的本身的。
2.new
的方法通过源码知道是 callAlloc
方法和 init
的方法的结合
+ (id)new {
return [callAlloc(self, false/*checkNil*/) init];
}
提供一份可以调试的runtime
版本
链接:https://pan.baidu.com/s/1MOwok3lAMOS2hvxTqoqx3A
密码:jqw0