美文网首页OC底层
alloc&init探索

alloc&init探索

作者: H丶ym | 来源:发表于2020-09-13 11:09 被阅读0次

OC底层原理学习

alloc 初探

首先创建一个继承于NSObjectPerson
带着以下几个疑问,查看打印结果
1 alloc 做了什么?
2 init 做了什么?

    Person *p1 = [Person alloc];
    Person *p2 = [p1 init];
    Person *p3 = [p1 init];
    NSLog(@"p1 地址:%@ 指针地址 %p",p1,&p1);
    NSLog(@"p2 地址:%@ 指针地址 %p",p2,&p2);
    NSLog(@"p3 地址:%@ 指针地址 %p",p3,&p3);

结论1

  • alloc时开辟了一块内存创建好了对象
  • p1、p2、p3 三个不同的指针,指向了同一块内存

此次新的疑问

3 alloc是怎么开辟内存的?
4 栈内存 是连续的?

根据上一篇OC 探索底层原理已经可以运行起来objc4-781,跟源码,来解决我们之前的疑问

一、alloc的源码

step 1.首先来到NSObject.mm

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

step 2. 来到NSObject.mm 中的 _objc_rootAlloc方法

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

step 3. 来到NSObject.mm 中的 callAlloc方法

static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
#if __OBJC2__
    if (slowpath(checkNil && !cls)) return nil;
    if (fastpath(!cls->ISA()->hasCustomAWZ())) {
        return _objc_rootAllocWithZone(cls, nil);
    }
#endif

    // No shortcuts available.
    if (allocWithZone) {
        return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil);
    }
    return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc));
}

看到了两个平时没怎么见过的宏 slowpathfastpath

#define fastpath(x) (__builtin_expect(bool(x), 1))
#define slowpath(x) (__builtin_expect(bool(x), 0))

其中的__builtin_expect指令是由gcc引入的,
1、目的:编译器可以对代码进行优化,以减少指令跳转带来的性能下降。即性能优化
2、作用:允许程序员将最有可能执行的分支告诉编译器。
3、指令的写法为:__builtin_expect(EXP, N)。表示 EXP==N的概率很大。
4、fastpath定义中__builtin_expect((x),1)表示x的值为真的可能性更大;即 执行if里面语句的机会更大
5、slowpath定义中的__builtin_expect((x),0)表示 x 的值为假的可能性更大。即执行else 里面语句的机会更大
6、在日常的开发中,也可以通过设置来优化编译器,达到性能优化的目的,设置的路径为:Build Setting --> Optimization Level --> Debug -->None改为fastest 或者smallest
通过添加断点,得知这个方法最终走到了return _objc_rootAllocWithZone(cls, nil);

step 4. 来到objc-runtime-new.mm中的_objc_rootAllocWithZone方法

id
_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone __unused)
{
    // allocWithZone under __OBJC2__ ignores the zone parameter
    return _class_createInstanceFromZone(cls, 0, nil,
                                         OBJECT_CONSTRUCT_CALL_BADALLOC);
}

step 5. 来到objc-runtime-new.mm中的_class_createInstanceFromZone方法

static ALWAYS_INLINE id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
                              int construct_flags = OBJECT_CONSTRUCT_NONE,
                              bool cxxConstruct = true,
                              size_t *outAllocatedSize = nil)
{
    ASSERT(cls->isRealized());

    // Read class's info bits all at once for performance
    bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();
    bool hasCxxDtor = cls->hasCxxDtor();
    bool fast = cls->canAllocNonpointer();
    size_t size;
    //计算需要开辟的内存空间
    size = cls->instanceSize(extraBytes);
    if (outAllocatedSize) *outAllocatedSize = size;

    id obj;
    if (zone) {
        obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
    } else {
        // 打断点走到这里
        // alloc 开辟内存,这里的obj是最后方法的返回值
        obj = (id)calloc(1, size);
    }
    if (slowpath(!obj)) {
        if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
            return _objc_callBadAllocHandler(cls);
        }
        return nil;
    }

    if (!zone && fast) {
        // 打断点走到这里
        //将cls类与isa指针关联
        obj->initInstanceIsa(cls, hasCxxDtor);
    } else {
        // Use raw pointer isa on the assumption that they might be
        // doing something weird with the zone or RR.
        obj->initIsa(cls);
    }

    if (fastpath(!hasCxxCtor)) {
        return obj;
    }

    construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;
    return object_cxxConstructFromClass(obj, cls, construct_flags);
}

跟进开辟内存的方法calloc,来自源码站,进不去了😀

void    *calloc(size_t __count, size_t __size) __result_use_check __alloc_size(1,2);

加几个断点分析一下这个方法里的代码

1.添加第一个断点,同时控制台打印obj

2.添加第二个断点,同时控制台再次打印obj

结论
在第一个断点打印,只有地址,在第二个断点打印有了类信息,由此可以推算出calloc开辟内存,initInstanceIsa创建isa指针,并且跟内存地址关联!我们现在只分析流程,具体开辟多少内存,isa是怎么创建的我们下一章聊

alloc 流程图

二、init的源码

step 1.首先来到NSObject.mm

- (id)init {
    return _objc_rootInit(self);
}

step 2.来到NSObject.mm中的_objc_rootInit

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;
}

结论
init 只是返回本身,它只是一个构造方法 ,是通过工厂模式,方便开发者自定义构造函数

三、new的源码

有的人创建对象时,不写alloc init,而是直接写new

+ (id)new {
    return [callAlloc(self, false/*checkNil*/) init];
}

结论
我们看到它调用了callAllocinit方法,和 alloc init在底层本质是一样的,但是如果你自定义了初始化方法,比如initWithName,那么它可不会帮你掉用initWithName,所以还是不建议用new

相关文章

  • isa的初始化&指向分析

    在探索alloc&init一篇中,我们对alloc&init有了初步的了解,其中包括对isa的初始化,本篇我们进一...

  • alloc&init探索

    OC底层原理学习 alloc 初探 首先创建一个继承于NSObject的Person类带着以下几个疑问,查看打印结...

  • alloc&init探索

    main函数的加载流程 1.在int main前面打个断点 然后增加一个_objc_init的符号断点 记得关闭左...

  • alloc&init 探索

    alloc&init 探索 首先要明确alloc做了什么,init做了什么。 上方的p1/p2/p3经打印是一模一...

  • iOS 底层探索 文章汇总

    一、alloc&init探索[https://www.jianshu.com/p/d6833d5d8b09]二、i...

  • 一、alloc&init探索

    objc源码官方地址分析源码,先从创建对象开始 1、alloc做了什么?开辟内存空间2、init做了什么? 3、n...

  • alloc&init底层探索

    [[NSObject alloc] init]两段式构造 1.对象分配,方法有alloc何allocWithZon...

  • alloc&init原理探索

    一、调试方法: 开篇我们先来介绍三种可以进入libobjc.A.dylib(objc_alloc所在的动态库)的调...

  • iOS-OC对象原理_alloc&init

    alloc&init探索 NSLog结果: 用图表示大概是这个样子: 表示我是谁(我在栈上的位置), 表示我指向的...

  • OC底层-alloc&init探索

    前言 我们创建一个对象经常用到alloc、init或者new,也大概知道alloc做了分配内存,init做了初始化...

网友评论

    本文标题:alloc&init探索

    本文链接:https://www.haomeiwen.com/subject/gqknektx.html