OC 对象的本质01

2018-08-24  本文已影响0人  SKY_Tang

问题:一个NSObject对象占用多少内存

编译过程

由编译过程可知Objective-c的面对对象都是基于 C\C++的数据结构来实现的,那首先弄清楚 Objective-c的对象、类主要基于 C\C++的什么数据结构来实现的?

将Objective-c代码转换成 C\C++的代码

Objective-c代码

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSObject *objc = [[NSObject alloc] init];
    }
    return 0;
}

转换命令:

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp

xcrun -sdk 平台 clang -arch 架构 -rewrite-objc 源文件 -o 输出文件
平台: windows,macos,os;不同平台支持的代码肯定不一样
架构:模拟器(i386),32bit(armv7),64bit(arm64)
如果需要链接其他框架,使用-framework参数(比如-framework UIKit)

在代码中找到 NSObject的C++实现

struct NSObject_IMPL {
    Class isa;
};

也就是NSObject类的Objective-c到 C++的实现是


NSObject类的Objective-c到 C++的实现

从中看出NSObject的C++实现是一个结构体,成员只有一个 class 类型的 isa,来看看 class 类型的实现

typedef struct objc_class *Class;

class 是 指向结构体类型的指针,也就是NSObject的C++实现是一个结构体,只包含一个指针类型的成员

指针类型内存大小:在32bit 是4个字节,在64bit 是8个字节

由此可知NSObject的C++实现的结构体内存大小是8个字节

假设 isa 的地址:0x100400110,那NSObject的C++实现的结构体地址也是0x100400110,也就是NSObject对象的地址也是0x100400110


NSObject对象地址

初步得出结论:一个NSObject对象占用8个字节

现在用class_getInstanceSizemalloc_size两个方法来打印下返回大小

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSObject *objc = [[NSObject alloc] init];
        
        NSLog(@"%zd", class_getInstanceSize([NSObject class]));

        NSLog(@"%zd", malloc_size((__bridge const void *)objc));
    }
    return 0;
}

打印结果:

2018-08-24 17:23:05.944980+0800 interview01-OC对象的本质[51693:837247] 8
2018-08-24 17:23:05.945510+0800 interview01-OC对象的本质[51693:837247] 16

可以看见malloc_size的结果是16,与NSObject的C++实现的结构体内存大小是8个字节不一致。
从苹果开源网站(opensource.apple.com)找到源码来看看class_getInstanceSizemalloc_size两个方法的不同

objc4 class_getInstanceSize
看看alignedInstanceSize()的实现
    // Class's ivar size rounded up to a pointer-size boundary.
    uint32_t alignedInstanceSize() {
        return word_align(unalignedInstanceSize());
    }

Class's ivar size rounded up to a pointer-size boundary.翻译过来就是返回类的成员变量的内存大小

allocWithZone
_objc_rootAllocWithZone
class_createInstance
_class_createInstanceFromZone
instanceSize
CF requires all objects be at least 16 bytes.翻译过来就是一个 oc 对象最少16个字节
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSObject *objc = [[NSObject alloc] init];
        
        // 获取NSObject实例对象的成员变量所占用的大小:8
        NSLog(@"%zd", class_getInstanceSize([NSObject class]));
        // 获取objc指针所指向分配内存的大小:16
        NSLog(@"%zd", malloc_size((__bridge const void *)objc));
    }
    return 0;
}

答案:

系统分配了16个字节给 NSObject对象(通过 malloc_size 函数获得)

但NSObject对象内部只使用了8个字节的空间(64bit 环境下,通过 class_getInstanceSize 函数获得)

从内存的角度来窥探本质

断点打印 objc 的内存地址

可以看到对象 objc 的地址是0x100450eb0,进入View Memory 工具查看内存

View Memory View Memory 数据格式

数据格式说明:

  1. 最左边红色框内显示的是内存地址
  2. 地址以十六进制表示编号,内存的基本单位是字节(8bit)
  3. 每一个字节对应一个地址
  4. 系统分配内存的逻辑,分配的内存大小时赋值00,初始化的时候再具体赋值

可以看到0x100450eb0分配的内存明显是41 31 FC B3 FF FF 1D 00 00 00 00 00 00 00 00 00,也就是16个字节,但真正利用起来的只有8个字节。

拓展lldb 命令

  1. print、p:打印
  2. po:打印对象
  3. 读取内存

    memory read/(数量)(格式)(字节)
    x/(数量)(格式)(字节)
    格式:x是16进制,f是浮点,d是10进制
    字节大小:b:byte 1字节,h:half word 2字节,w:word 4字节,g:giant word 8字节

读取`0x100450eb0`开始的4个8字节16进制的内存 读取`0x100450eb0`开始的3个4字节10进制的内存
  1. 修改内存中的值

    memory write 内存地址 数值

修改`0x100450eb9`的值为4
上一篇 下一篇

猜你喜欢

热点阅读