iOS底层探索alloc流程

准备工做

1.苹果objc源码下载算法

2.源码编译xcode

3.准备调试代码:缓存

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface ABPerson : NSObject

@end

NS_ASSUME_NONNULL_END

复制代码
#import "ABPerson.h"

@implementation ABPerson

@end

复制代码
#import "ViewController.h"
#import "ABPerson.h"
@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    ABPerson *p1 = [ABPerson alloc];
    ABPerson *p2 = [p1 init];
    ABPerson *p3 = [p1 init];
    NSLog(@"%@ - %p - %p",p1,p1,&p1);
    NSLog(@"%@ - %p - %p",p2,p2,&p2);
    NSLog(@"%@ - %p - %p",p3,p3,&p3);
    
}

复制代码

底层探索的三种分析方法

第一种是经过符号断点去定位

1.添加一个alloc符号断点,步骤以下: 图片.pngmarkdown

图片.png

图片.png

图片.png

2.在调试代码alloc那一行添加一个断点app

图片.png 3.运行项目,断点会断点下面这个位置,可是这不是本身代码alloc调用的,,由于在调用我准备代码alloc以前,系统也会调用alloc,初始化不少东西。函数

图片.png

4.取消下图断点以下:oop

图片.png

5.点击红框图标,执行下一步post

图片.png 6.再将刚才取消的断点,使其生效,以下:优化

图片.png

7.点击红框图标,执行下一步 图片.pngui

8.此时咱们看到libobjc.A.dylib库下的[NSObject alloc]就是本身代码alloc调用的,ABPerson继承NSObject 图片.png 9.接下来就能够经过库名称、方法名在objc源码中探索

第二种经过xcode step into和符号断点去定位

1.删除符号断点,运行项目,在当前断点位置,按住control键点击红框图标

图片.png 2.当出现调用objc_alloc时,下个objc_alloc的符号断点,执行下一步 图片.png

图片.png

图片.png

图片.png 3.获得库的名称了和函数名称了

第三种经过汇编和符号断点去定位

1.当断点断到本身的代码后,查看汇编 图片.png 2.在汇编下断点的位置,找到了函数调用 图片.png 3.下符号断点objc_alloc,方法如上

alloc探索

打开编译好的源码,搜索alloc { 图片.png 进入_objc_rootAlloc 图片.png 进入callAlloc 图片.png 若是!cls->ISA()->hasCustomAWZ()成立,执行_objc_rootAllocWithZone,再执行objc_msgSend

在ABPerson的工程中依次给alloc_objc_rootAlloccallAlloc_objc_rootAllocWithZone下符号断点

图片.png 图片.png 图片.png 发现断点并无断到callAlloc,这是编译器作了优化。

编译器优化案例

#import <Foundation/Foundation.h>

int ABSum(int a,int b){
    return  a + b;
}
int main(int argc, const char * argv[]) {
   
    int a = 10;
    int b = 20;
    int c = ABSum(a, b);
    NSLog(@"----%d",c);
    return 0;
}
复制代码

汇编查看:

图片.png 这是未优化的,callq调用了ABSum函数

xcode配置优化等级 图片.png 再次运行

图片.png 直接使用$0x1e,也就是30,并未调用ABSum函数,被优化了。

解释了编译器优化,接下来看_objc_rootAllocWithZone实现

图片.png 跟进去看一下_class_createInstanceFromZone实现

图片.png

objc源码调试

1.源码工程编译target选择KCObjcBuild

图片.png

2.查看main.m文件,修改代码:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        LGPerson *p = [LGPerson alloc] ;
        NSLog(@"%@",p);
    }
    return 0;
}
复制代码

3.修改LGPerson代码:

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface LGPerson : NSObject

@end

NS_ASSUME_NONNULL_END

复制代码

4.打上断点运行工程

图片.png 5.按照上面断点调试方式,确保_class_createInstanceFromZone是被LGPerson调用

图片.png

6.step into instanceSize

图片.png 若是有缓存就返回缓存的大小。

7.step into fastInstanceSize

图片.png 图片.png 拿到size 再经过align16,16字节对齐 16字节对齐算法,当x=16时,

(16+15)& ~15

31&~15

31 的二进制表示:0001 1111

15 的二进制表示:0000 1111

~15的二进制表示:1111 0000

31&~15: 0001 0000

十进制表示就是16

8.没有就经过alignedInstanceSize计算

图片.png 经过unalignedInstanceSize拿到实例对象的实际大小,再经过word_align进行8字节对齐

图片.png

图片.png

8字节对齐算法: 当前LGPerson并无成员变量,只继承了NSObjctisa,因此x=8

(8+7)& ~7

15&~7

15 的二进制表示:0000 1111

7 的二进制表示:0000 0111

~7的二进制表示:1111 1000

15&~7: 0000 1000

十进制表示就是8

计算的结果还要再作一次处理,至少要16个字节

图片.png

对象的内存空间

1.修改LGPerson代码:

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface LGPerson : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic) int age;
@property (nonatomic) int height;
@property (nonatomic, copy) NSString *nickName;

@end

NS_ASSUME_NONNULL_END
复制代码

2.修改main.m代码:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        LGPerson *p = [LGPerson alloc] ;
        p.name      = @"Cooci";
        p.nickName  = @"KC";
        p.age       = 18;
        p.height    = 180;
        NSLog(@"%@",p);

    }
    return 0;
}

复制代码

图片.png

图片.png

可以看到前8个字节存储isa相关信息,age和height共用8个字节就够了,因此LGPerson的实例对象占用了32个字节大小。

instanceSize执行完后,须要一个obj做为返回object_cxxConstructFromClass的参数 图片.png 默认obj是一个脏地址,当执行了calloc,会对它进行从新赋值。

接着往下看 图片.png

图片.png 最后都会调用initIsa关联对象

alloc流程图

图片.png

相关文章
相关标签/搜索