OC 底层研究1--alloc和init原理


本页所使用的objc runtime 756.2,来自GITHUB

1. 题目

开始学习研究OC源码,今天看的是对象的初始化,也就是alloc以及init的分析。

2. 附流程图如下

3. 内容

3.1引子

我们先看一段代码,Person类的初始化,打印p1,p2,p3对象,以及指向他们的指针:

    Person *p1 = [Person alloc];
    Person *p2 = [p1 init];
    Person *p3 = [p1 init];

    NSLog(@"%@ -- %p", p1, &p1);
    NSLog(@"%@ -- %p", p2, &p2);
    NSLog(@"%@ -- %p", p3, &p3);

打印结果是:

这里为什么p1相同的情况下,经过init后的p2,p3 的地址都不一样呢,我们要用这个图来分析:

在这里,我们[Person alloc]创建了一个p 对象的内存空间,而[p1 init]则是创建同样是指向p1的指针p2,p3,因为和p1 内存空间一致,所以地址均为0x6000024f4950,但是指向该3个指针的指针不一样,所以&p1, &p2, &p3 地址不一致。

3.2源码分析:

alloc 步骤如下

1. alloc

该步骤主要为创建对象,申请内存空间。相关objc源代码如下

+ (id)alloc {
    return _objc_rootAlloc(self);
}

  

2. objc_rootAlloc

基类对于alloc 的实现,此时cls不为空,源码如下:

init方法:

类的init 如下

+(id)init{
        return (id)self;
}

对象的init如下

- (id)init{
        return _objc_rootAlloc(self);
}
id
_objc_rootAlloc(Class cls)
{
    return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}

此处init 方法仅仅为工厂初始方法,作为父类方法,方便子类重写。

3. callAlloc**

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];
}
4. class_createInstance

如上代码,如无捷径,类对象即创建实例

class_createInstance(Class cls, size_t extraBytes)
{
    return _class_createInstanceFromZone(cls, extraBytes, nil);
}
5. _class_createINstanceFramZone
  1. Cls->instanceSize

    此时为对其寻址空间,统一为为每个对象开辟16位的位置,防止溢出。

        size_t size = cls->instanceSize(extraBytes);
        // CF requires all objects be at least 16 bytes.
        if (outAllocatedSize) *outAllocatedSize = size;
  2. calloc

    如果空间已开辟,则指定空间并返回,类似去学校报道,宿管阿姨带你去宿舍并交付钥匙给你,代码如下:

    
            if (zone) {
                // malloc_zone_calloc 即为开辟内存
                obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);
            } else {
                obj = (id)calloc(1, size);
            }
  3. objc->initInstanceIsa

    这一步为确定指针,即给宿舍贴上门牌号,相关代码如下:

        if (!zone  &&  fast) {
            obj = (id)calloc(1, size);        // 该行开辟了名为obj空间
            if (!obj) return nil;
            obj->initInstanceIsa(cls, hasCxxDtor);
        } 

在这一环节,obj = (id)calloc(1, size) 该行开辟了名为obj空间。

obj->initInstanceIsa(cls, hasCxxDtor) 则真正的将内存空间obj 与类cls 进行关联上。

4. 总结

总的来说,alloc的过程,即时在堆区开辟空间给对象,并在栈区开辟指针(大小为8字节)指向该对象的内存区,即给定isa,以方便寻址。

以上,如果有更深入的理解,会再来补充,供参考。


文章作者: 李佳
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 李佳 !
评论
 上一篇
OC底层研究2--内存对齐原理 OC底层研究2--内存对齐原理
本页所使用的objc runtime 756.2,来自GITHUB 开始继续学习研究OC源码,今天看的是OC的内存原理,以及对齐原理的分析。 1.概念内存对齐在数据结构中,是比较基础也比较重要的一环,对于iOS开发,研究底层更免不了要了
2019-12-22 李佳
下一篇 
使用Runtime获取第三方成员变量 使用Runtime获取第三方成员变量
我们使用第三方库,有时候需要获取其成员变量。方法如下: 导入runtime #import <objc/runtime.h> 声明一个Person类 @interface Person : NSObject { NSStrin
2019-12-12 李佳
  目录