一、alloc&init探索

objc源码官方地址
分析源码,先从创建对象开始

    LGPerson *p1 = [LGPerson alloc];
    LGPerson *p2 = [p1 init];
    LGPerson *p3 = [p1 init];
    //输出 内容、内存地址、指针地址
    LGNSLog(@"%@ - %p - %p",p1,p1,&p1);
    LGNSLog(@"%@ - %p - %p",p2,p2,&p2);
    LGNSLog(@"%@ - %p - %p",p3,p3,&p3);

打印结果:
 - 0x6000028bcb40 - 0x7ffee41000e8
 - 0x6000028bcb40 - 0x7ffee41000e0
 - 0x6000028bcb40 - 0x7ffee41000d8
alloc.png

1、alloc做了什么?
开辟内存空间
2、init做了什么?

//init 没有对空间做任何修改
+ (id)init {
    return (id)self;
}

3、new做了什么?

+ (id)new {
    return [callAlloc(self, false) init];
}  
// 等同于 (alloc + init)
//不建议直接new, 因为可能会重写 init ,init 可能会带参数

4、alloc怎么开辟空间的?
主要是 _class_createInstanceFromZone 的实现流程,分三步:
cls->instanceSize :计算需要的内存空间大小(16字节对齐)
calloc :向系统申请开辟内存,返回地址指针
obj->initInstanceIsa:将类和内存地址绑定在一起,实例的isa指向类

5、alloc 的流程图

alloc源码流程图.png

6、栈内存 是连续的?
栈内存是连续的,一个对象是8字节

7、NSObject *objc = [NSObject alloc] 为什么没有进+ (id)alloc {..}方法?

NSObject.mm
//LLVM (编译启动): 任何级别调用的alloc方法,都会调用到objc_alloc方法
// Calls [cls alloc].
id objc_alloc(Class cls)
{
    return callAlloc(cls, true/*checkNil*/, false/*allocWithZone*/);
}

扩展:
1、命令
bt:打印堆栈
x 地址: 查看当前地址的内存情况
x/4gx 地址: 以16进制的形式打印4个地址
x/8gx 地址: 以16进制的形式打印8个地址
po 变量:显示内存的内容

2、字节对齐

//字节对齐算法, 16的倍数
//1.方便速度便捷,2. 16更安全
static inline size_t align16(size_t x) {
    return (x + size_t(15)) & ~size_t(15);
}

任何一个对象都有一个isa ,isa 占8字节,如果按8字节对齐,每个对象都紧诶着其他对象的isa,会容易出现混乱, 为了内存安全,预留空间。 为什么是16?一个对象8字节,以最小单元翻倍。因为如果以一个9或10就会很恶心,需要随时改变读取的长度。每次都按8或16来读,每次都读这么多,不会产生混乱。

3、看源码的3种方式

  • 下符号断点
    第一步:在[LGPerson alloc]加断点,并运行
    第二步:下符号断点


    第一步:[LGPerson alloc]加断点.png
第二步:下符号断点的位置.png
符号断点.png
结果:alloc所在的库.png
  • control + (step into)
    第一步:在[LGPerson alloc]加断点
    第二步:control + (step into)
    第三步:下符号断点


    第二步:control + (step into).gif
第三步:添加符号断点objc_alloc.png
  • 最常用方法:汇编查看流程
    第一步:在[LGPerson alloc]加断点
    第二步:debug ->debug workflow -> always show assembly
    第三步:下符号断点


    第二步:打开汇编查看.png
第三步:添加objc_alloc符号断点.png

你可能感兴趣的:(一、alloc&init探索)