前言
探索下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

3万+

被折叠的 条评论
为什么被折叠?



