OC底层原理(一)alloc&init&new原理分析

最新alloc源码调用流程

查看alloc调用底层的方法

通过查看汇编分析,然后在通过汇编分析的方法添加符号断点查看

先将在alloc初始化处添加断点

然后Debug->Debug Workflow->Always Show Disassembly来查看汇编中的调用

查看汇编,当前会停在我们当前要执行的函数,查看汇编发现,想下会调用_objc_alloc方法

此时我们在通过添加符号断点,将_objc_alloc添加

继续执行,我们发现确实执行了objc_alloc方法,接下来我们需要到objc源码中查看后面的流程

查看objc源码

首先我们通过一个可以运行的objc项目来进行演示,可运行的objc项目在文章末尾自行下载

我们通过按commd+鼠标左键进入alloc的调用,发现就直接进入到了objc的alloc方法

alloc中调用_objc_rootAlloc

objc_rootAlloc继续调用callAlloc

callAlloc会调用_objc_rooAllocWithZone方法

_objc_rooAllocWithZone中会调用_class_createInstanFromZone方法进行内存分配和实例对象创建

_class_createInstanFromZone首先会根据传入对象的属性来计算内存大小,然后在创建一个size大小obj对象,最后将obj对象与cls也就是我们传入的类进行关联,最后将创建好的对象返回

内存对齐

计算机编码过程中内存分配是有一定规则的,一般长以8的倍数进行分配,但是iOS中比较特殊是以16的倍数进行分配的。

由于内存比较小,所以内存在分配时内存地址一般是连续的,内存读取的方式是以块状读取,那么当内存按照最小的块大小进行倍数分配时,当读取的时候可以防止读取出错。

iOS对象都有一个isa指针,isa就是对象在内存的首地址,每个指针占8字节,但是对象还可能有自己的属性,那么初始的收我们就默认分配的最小内存就是16字节,而且内存是按16的倍数进行对齐。

下面我们查看源码:

如果计算出来的内存如小于16字节,那么直接返回16字节

内存对齐计算方式:x+size_t(15))&~size_t(15),将计算的内存大小与上一个按位取反的15

如计算出内存需要分配20那么内存对齐过程如下

20的二进制 0000 0000 0001 0100

15 的二进制 0000 0000 0000 1111 +

————0000 0000 0010 0011

~15 二进制 -1111 1111 1111 0000 &

————0000 0000 0010 0000 == 32

所以会20字节内存对齐后会分配32字节

init源码

通过源码查看,init返回的其实就是自身对象,不会创建新的对象

那么为什么还要有init方法呢?

init方法是我们对象初始化时的构造方法,开发者可通过重写init方法,来重构自己的构造方法,在初始化的时候可以带入自己自定义的参数来快速初始化对象。

new源码

我们可以看到,new实际上就是直接调用alloc后又帮我们调用了一次init方法

其实new防反就等同于alloc + init方法

看完原理看代码

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        YJCar *car1 = [YJCar alloc];
        YJCar *car2 = [car1 init];
        YJCar *car3 = [car1 init];
        
        NSLog(@"%@--%p--%p", car1, car1, &car1);
        NSLog(@"%@--%p--%p", car2, car2, &car2);
        NSLog(@"%@--%p--%p", car3, car3, &car3);
    }
    return 0;
}
复制代码

证明此时car1、car2、car3是同一个对象,都引用这car1

通过代码log打印方式

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        YJCar *car1 = [YJCar alloc];
        YJCar *car2 = [car1 init];
        YJCar *car3 = [car1 init];
        car1.name = @"奔驰";
        
        NSLog(@"%@--%@--%@", car1.name, car2.name, car3.name);
        car3.name = @"BMW";
        NSLog(@"%@--%@--%@", car1.name, car2.name, car3.name);
    }
    return 0;
}
复制代码

通过窥探内存方式

(lldb) x car1
0x6000020ff780: e8 55 15 00 01 00 00 00 40 00 15 00 01 00 00 00  .U......@.......
0x6000020ff790: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
(lldb) x/4g car1
0x6000020ff780: 0x00000001001555e8 0x0000000100150040
0x6000020ff790: 0x0000000000000000 0x0000000000000000
(lldb) x/4g car2
0x6000020ff780: 0x00000001001555e8 0x0000000100150040
0x6000020ff790: 0x0000000000000000 0x0000000000000000
(lldb) x/4g car3
0x6000020ff780: 0x00000001001555e8 0x0000000100150040
0x6000020ff790: 0x0000000000000000 0x0000000000000000
(lldb) po 0x0000000100150040
奔驰
复制代码

看到这里在回答面试官的int的初始化过程和init与alloc及new的关系就可以向切菜一样了吧

我也是在重新复习底层知识,如果本文对大家有帮助希望,希望大家收藏点赞。

也欢迎与各位大佬互动探讨,谢谢!

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享