OC底层原理一:OC对象的本质

15 阅读3分钟

OC源码下载地址:opensource.apple.com/tarballs/ob…

一、内存大小

  1. iOS系统分配内存的原则一:分配的内存大小至少16字节

获取系统给OC对象实际分配的内存大小:malloc_size(需#import <malloc/malloc.h>)

NSLog(@"%zd", malloc_size((__bridge const void *)(obj)));

从NSObject的底层实现代码可以看出,NSObject是一个结构体,里面有一个指针成员(指针在64位系统占8字节,在32位系统占4字节),因此一个NSObject对象占8字节。

问:一个NSObject对象占多大内存?

答:iOS系统给NSObject对象分配16字节的内存,但是在64bit环境下,NSObject对象只使用8字节。

  1. iOS系统分配内存的原则二:分配的内存大小是16的倍数

一个Student类继承自NSObject,有3个int类型的成员:

获取类的成员变量的实际使用的内存大小:class_getInstanceSize(需#import <objc/runtime.h>)

NSLog(@"成员变量的内存大小:%zd", class_getInstanceSize([Student class]));

打印结果是24字节,由于这个原则,iOS系统会给Student对象分配32字节,而实际只使用24字节。

  1. 结构体的内存对齐原则:结构体的大小必须是最大成员变量大小的倍数

如上图,这个结构体的大小必须是8的倍数,也是就是16。

  1. 分配的内存大小至少16字节的验证过程

  2. 分配的内存大小是16的倍数的验证过程

malloc源码下载地址:opensource.apple.com/tarballs/li…

分配内存的函数调用流程:

(objc源码)allocWithZone: --> _objc_rootAllocWithZone --> class_createInstance --> _class_createInstanceFromZone --> malloc_zone_calloc

(libmalloc源码) malloc_zone_calloc --> nano_calloc(从ptr = zone->calloc(zone, num_items, size);推断出,可能不正确) --> _nano_malloc_check_clear --> segregated_size_to_fit

segregated_size_to_fit函数如下:

代码解析:

SHIFT_NANO_QUANTUM:16

SHIFT_NANO_QUANTUM:4

假设传入24,计算过程如下:

24+16-1 = 39

39的二进制:             0010 0111

往右移4位(>>4): 0000 0010 (后面的被舍弃)

往左移4位(<<4): 0010 0000 == 2^5 == 32

先把size+16-1,使其大于16,然后往右移4位(移除那些十进制值小于16的位),再往左移4位,得到 16的倍数(前面四位的值都是16的倍数),整个操作的目的就是得到比该值大,并且是16倍数的值。 

所以得出结论:iOS中分配内存的大小是16的倍数。

  1. 查看某种类型占多大内存:sizeof

如:sizeof(int)

二、LLDB命令

 LLDB的Xcode默认的调试器,它与LLVM编译器一起,带给我们更丰富的流程控制和数据检测的调试功能。

  1. 常用指令
  • p:打印(print的缩写)
  • po:打印对象(print object的缩写)
  • memory read:读取内存(简写:x)
    1. 使用:x/数量+格式+字节数 对象的内存地址

如:x/3xw 0x10010,表示:读取0x10010对象的内存(x),以16进制(x)格式输出3条数据(3),每条数据长度是4字节(w)。

    1. 格式说明:x:16进制,f:浮点,d:10进制
    2. 字节大小说明:b:byte 1字节,h:half word 2字节, w:word 4字节,g:giant word 8字节
  • memory write:修改内存的值

使用:memory write 0x10012 10,表示修改0x10012内存地址的值为10

  1. 实操

  2. 在Xcode查看实例的内存布局

1个十六进制 == 4个二进制

十六进制:0x1111 1111 1111 1111

二进制:0b1111

2个十六进制 == 8个二进制(1位表示1个二进制) == 8位 == 1字节

前面8个字节被isa指针占用,后面8字节由于没有数据,初始化为0。