内存V10(逻辑教育)

iOS alloc 流程分析

2020-09-05  本文已影响0人  differ_iOSER

iOS 底层探索 文章汇总

一、思考

  1. alloc 到底做了什么事?
  2. init 有什么用?
  3. [[Class alloc] init] 与 new有什么区别?
WX20200905-111912.png

通过这段代码,我们可以看出p1、p2、p3的指针地址虽然不同,但是指向的对象却是同一个,也就是同一片内存空间。

+ (instancetype)alloc OBJC_SWIFT_UNAVAILABLE("use object initializers instead");

二、alloc源码分析

准备工作

1.从 苹果官方开源代码列表 找到 objc4源码,但是这个源码是不可以编译的,需要编译的源码可在这里下载、配置源码可编译调试可参考这里

2.进入alloc方法可看到:

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

3.跟踪代码,调用方法如下:


方法调用.png

4.具体分析_class_createInstanceFromZone方法


_class_createInstanceFromZone.png

5.因此,我们就可以确定alloc至少做了两件事:\color{red}{开辟内存、初始化isa指针并关联类}

6.没属性的对象为什么会占用内存?
每个对象都有一个隐藏属性isa 每个指针占8个字节---目前系统分配空间是以16字节为单位,通过字节对齐。一个对象只有isa指针也会占16个字节

具体的内存对齐原则的相关描述可以查看:iOS内存对齐原则
需要注意的是目前采用的是16字节对齐

三、既然alloc将开辟内存关联类型都做了,那么init又是在干嘛呢?

1.定位代码:

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

_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;
}
  1. \color{red}{可见init仅仅只是将alloc开辟的对象直接返回了,是一种工厂化的设计,方便我们对类进行重写。}

四、那么new 与[[Class alloc] init] 有什么区别呢?

1.定位代码

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

callAlloc(Class cls, bool checkNil, bool allocWithZone=false)  //new 使用默认参数
{
#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));
}


// Base class implementation of +alloc. cls is not nil.
// Calls [cls allocWithZone:nil].
id
_objc_rootAlloc(Class cls)
{
    return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);//alloc的参数allocWithZone为true
}

2.可见new和[[Class alloc] init],唯一区别就是allocWithZone参数为false
iOS 中 alloc init 和new的区别


扩展一、fastpath 和 slowpath

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

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

__builtin_expect(exp, n) 方法表示 exp 很有可能为 0,返回值为 exp。
fastpath意思是:bool(x)为真的概率很大
slowpath意思是: bool(x)为真的概率很小

首先要明确:

if(fastpath(value))  //等价于 if(value)
if(slowpath(value))  //也等价于 if(value)

也就是说,使用fastpath(),执行 if 后面的语句的机会更大,使用 slowpath(),执行 else 后面的语句的机会更大。通过这种方式,编译器在编译过程中,会将可能性更大的代码紧跟着起面的代码,从而减少指令跳转带来的性能上的下降。

其实将fastpath和slowpath去掉是完全不影响任何功能的。之所以将fastpath和slowpath 放到if语句中,是为了告诉编译器,if中的条件是大概率(fastpath)还是小概率(slowpath)事件,从而让编译器对代码进行优化。参考

扩展二、[[NSObject alloc] init]和[[LGPerson alloc] init]差异

NSObject *objc1 = [[NSObject alloc] init];
方法调用如下:

// Calls [[cls alloc] init].
id
objc_alloc_init(Class cls)
{
    return [callAlloc(cls, true/*checkNil*/, false/*allocWithZone*/) init];
}

LGPerson *objc2 = [[LGPerson alloc] init];
方法调用如下:

// Calls [[cls alloc] init].
id
objc_alloc_init(Class cls)
{
    return [callAlloc(cls, true/*checkNil*/, false/*allocWithZone*/) init];
}


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


// Base class implementation of +alloc. cls is not nil.
// Calls [cls allocWithZone:nil].
id
_objc_rootAlloc(Class cls)
{
    return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}

然后调用
- (id)init {
    return _objc_rootInit(self);
}

流程图如下:

WX20200905-215003.png

具体流程图如下:


1616482-20d480c0d97b38b9.png

流程图参考:陪妮走未来

文章参考一:NSObject +alloc做了什么
文章参考二:OC踩坑第一篇:alloc流程初探

上一篇 下一篇

猜你喜欢

热点阅读