iOS开发-NSObject的alloc函数执行

前言

探索下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
    其中fastpathslowpath 就是告诉编译器,bool(x)最可能的结果为1 或为0 ,这样寄存器读取指令时就默认优先读取if_else中的YES代码部分或者NO代码部分,达到优化目的。
#define fastpath(x) (__builtin_expect(bool(x), 1))
#define slowpath(x) (__builtin_expect(bool(x), 0))

就是likelyunlikely的实现,可以看 这篇博文

  • cls->ISA()->hasCustomAWZ()
    hasCustomAWZ 方法意思就是判断是否实现自定义的allocWithZone方法,如果没有实现就调用系统默认的allocWithZone方法。

Tips:这里需要注意的是,cls->ISA 有没有想过类的isa初始化是什么时候,实例的isa初始化是在alloc的时候,类的isa的初始化是在load_images方法执行时的

  • hasCxxCtorhasCxxDtor 表示是否有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执行步骤如下:

  1. 执行 objc_alloc
  2. 执行 callAlloc(cls,true,false)
  3. 判断是否实现自定义的allocWithZone方法,实现了就直接转到 5,没有实现就执行 alloc 方法,转到 4
  4. 执行 callAlloc(cls,false,true) ,并执行系统默认的allocWithZone方法
  5. 执行 class_createInstance
  6. 执行 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值