ios对象的底层探索(上)

291 阅读3分钟

0a000581fca74098bc53b876f5e957ae.jpeg

在iOS开发的过程中,我们最熟悉的就是对象,经常会使用到的一个函数:alloc,那这个函数的底层到底做了什么呢 ?我们一起一探究竟。

  1. 常用指令

1. po:   为 print object 的缩写,显示对象的文本描述
2. bt:   打印函数的堆栈  
3. register read    读取寄存器
4. x/nuf 
    n表示要显示的内存单元的个数
    u表示一个地址单元的长度:
    取值范围: 
            b 单字节  
            h 表示双字节
            w 表示四字节
            g 表示八字节
    f表示显示方式:
    取值范围:
            x 按十六进制格式
            d 按十进制格式
            u 按十进制格式显示无符号
            o 按八进制格式
            t 按二进制格式
            a 按十六进制格式
            i 指令地址格式  
            c 按字符格式
            f 按浮点数格式

持续更新中...
  1. 创建一个熟悉的对象

image.png

**2022-04-16 18:14:47.784118+0800 SXObjcDebug[27806:312258] <NYPerson: 0x100b39ba0>-0x100b39ba0-0x7ff7bfeff310**

**2022-04-16 18:14:47.784578+0800 SXObjcDebug[27806:312258] <NYPerson: 0x100b39ba0>-0x100b39ba0-0x7ff7bfeff300**
**2022-04-16 18:14:57.634432+0800 SXObjcDebug[27806:312258] <NYPerson: 0x100b39ba0>-0x100b39ba0-0x7ff7bfeff308**

通过调试代码,我们发现p1,p2,p3都是打印内存0x100b39ba0&p1,&p2,&p3打印的是不一样的。 我们通过objc4-838.1 看看alloc,init 做了什么。(也可以通过符号断点跟踪Symbolic Breakpoint)

  1. alloc的流程图

image.png image.png

通过调试验证:

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

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

image.png 然后我们进入了objc_alloc (那么这边有一个问题,alloc里面调的是_objc_rootAlloc。这是为什么呢,我们一步一步来。)

callAlloc中 ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc)) 消息转发到 alloc

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

}

第二次到了callAlloc 中执行_objc_rootAllocWithZone->_class_createInstanceFromZone 最后在_class_createInstanceFromZone进行内存分配。

  1. _class_createInstanceFromZone分析

我们看到了_class_createInstanceFromZone 中计算内存的方法 size= cls->instanceSize(extraBytes);

我们跟踪到具体计算方法:

image.png 根据cpu位数是32位64位计算。

如:(8 + 7) & ~7  8字节对齐,取8的整数倍
(8 + 7) >> 3 << 3
0000 1001 
&
1111 1000
0000 1000 = 8

为什么是8位呢,用8字节取读不断变化的空间连续内存,空间换取时间。

  1. 通过struct 结构体分析内存对齐

image.png 下面我们通过计算 struct1,struct2,struct3 的字节数,验证字节对齐。

image.png 通过字节对齐算法,我们计算得出 struct1=24,struct2=16,struct3=48

验证输出: image.png

  1. instanceSize流程

instanceSize流程.png

总结

综上的现象,我们可知alloc()方法实现了对象的内存分配,内存对齐,将对象和类型绑定三个功能。

内存对齐实际案例

  1. Apple在64位下,对象内存对齐是16,结构体是8。
  2. 内存分配时,会根据属性或成员变量的类型length, 属性或成员的起始内存必须是该类型length的整数倍。