示例一:Student添加成员变量
新建一个 Student 对象,对象中包含两个成员,代码如下:
#import <Foundation/Foundation.h> #import <objc/runtime.h> #import <malloc/malloc.h> @interface Student : NSObject { @public int _no; int _age; } @end @implementation Student @end int main(int argc, const char * argv[]) { @autoreleasepool { Student *stu = [[Student alloc] init]; stu->_no = 4; stu->_age = 5; NSLog(@"%zd", class_getInstanceSize([Student class])); NSLog(@"%zd", malloc_size((__bridge const void *)stu)); } return 0; }
那么这个 Student 对象拥有多少个占用多大内存呢?执行出来,得到的答案,两个都等于16个字节。
那么为什么是等于16个字节呢?我们还是先查看一下转C\C++的代码:
// 1、Student 的结构体 struct Student_IMPL { struct NSObject_IMPL NSObject_IVARS;// NSObject 的结构体 int _no;// 新成员1 int _age;// 新对象2 }; // 3、已知 NSObject 的结构体 struct NSObject_IMPL { Class isa; }; // 3、以上两个结构体,可以合成: struct Student_IMPL { Class isa;// 根据上一节,我们知道 isa 占用8个字节 int _no;// int 类型 占用4个字节 int _age; };
通过以上可知,Student 对象占用内存为:8 + 4 + 4 = 16个字节,刚好等于最小分配内存,所以 stu 实例对象占用内存值为16字节。
示例二:Student、Person 继承
#import <Foundation/Foundation.h> #import <objc/runtime.h> #import <malloc/malloc.h> @interface Person : NSObject { int _age; } @end @implementation Person @end @interface Student : Person { int _no; } @end @implementation Student @end int main(int argc, const char * argv[]) { @autoreleasepool { Student *stu = [[Student alloc] init]; NSLog(@"stu - %zd", class_getInstanceSize([Student class])); NSLog(@"stu - %zd", malloc_size((__bridge const void *)stu)); Person *person = [[Person alloc] init]; NSLog(@"person - %zd", class_getInstanceSize([Person class])); NSLog(@"person - %zd", malloc_size((__bridge const void *)person)); } return 0; }
错误分析:
struct Person_IMPL { struct NSObject_IMPL NSObject_IVARS; // 8 int _age; // 4 }; // 8 + 4 = 12 struct Student_IMPL { struct Person_IMPL Person_IVARS; // 12 int _no; // 4 }; // 12 + 4 = 16
但实际运行结果如下:
22-05-01 15:36:03.839551+0800 Interview01[3978:66568] stu - 16 2022-05-01 15:36:03.839981+0800 Interview01[3978:66568] stu - 16 2022-05-01 15:36:03.840062+0800 Interview01[3978:66568] person - 16 2022-05-01 15:36:03.840105+0800 Interview01[3978:66568] person - 16
为什么会这样呢?这里就要讲到原则:
1、内存对齐原则:结构体的最终大小必须是最大成员大小的倍数
2、OC(64位)对象最小分配内存为16字节。
正确分析:
struct Person_IMPL { struct NSObject_IMPL NSObject_IVARS; // 8 int _age; // 4 }; // 16 内存对齐:结构体的大小必须是最大成员大小的倍数 struct Student_IMPL { struct Person_IMPL Person_IVARS; // 16 int _no; // 4 }; // 16:因为 Person_IVARS 结构体还有4个字节还没有引用,刚好添加上去
示例三:Person 添加属性
#import <Foundation/Foundation.h> #import <objc/runtime.h> #import <malloc/malloc.h> @interface Person : NSObject { @public int _age; } @property (nonatomic, assign) int height; @end @implementation Person @end int main(int argc, const char * argv[]) { @autoreleasepool { Person *person = [[Person alloc] init]; [person setHeight:10]; [person height]; person->_age = 20; Person *person1 = [[Person alloc] init]; person1->_age = 10; NSLog(@"person - %zd", class_getInstanceSize([Person class])); NSLog(@"person - %zd", malloc_size((__bridge const void *)person)); } return 0; }
分析:
struct Person_IMPL { struct NSObject_IMPL NSObject_IVARS;//8 int _age;//4 int _height;// 4 };// 8 + 4 + 4 = 16
添加属性会自动执行 set和 get 方法,执行 set 的方法的时候,会自动成一个 _height 成员变量。
示例四、结构体变量占用大于16
@interface MJPerson : NSObject { int _age; int _height; int _no; } @end @implementation MJPerson @end int main(int argc, const char * argv[]) { @autoreleasepool { // 实例对象的所有成员变量的所占用的大小 24 NSLog(@"class_getInstanceSize:%zd", class_getInstanceSize([MJPerson class])); // 实例对象实际分配的大小 32 MJPerson *p = [[MJPerson alloc] init]; NSLog(@"malloc_size:%zd", malloc_size((__bridge const void *)(p))); } return 0; }
转换:
struct NSObject_IMPL { Class isa; }; struct MJPerson_IMPL { struct NSObject_IMPL NSObject_IVARS; int _age; int _height; int _no; }; // 计算结构体大小,内存对齐,24
结构体所占内存大小为24,那么实际 MJPerson *p 对象却是 32呢?
通过查看官网原码可知,苹果系统内存分配规则:
#define NANO_MAX_SIZE 256 /* Buckets sized {16, 32, 48, 64, 80, 96, 112, ...} */
根据往大了取的原则, MJPerson *p 对象实际分配了32个字节。
总结:
1、结构体内存对齐,是指在计算结构体大小的时候,遵循其分配的原则:结构体大小必须是最大成员变量分配内存的倍数。
2、iOS操作系统在分配内存的时候,也有内存对齐的概念,即为16的倍数 :在iOS的堆空间中,如果要创建一个OC对象,分配内存时,都是16的倍数。
拓展:
1、结构体最前面的成员的地址值就是就是这个结构体的地址值。
2、方法不放在实例对象中,而是放在类对象中。
3、创建一个实例对象,至少需要多少内存?
#import <objc/runtime.h> class_getInstanceSize([NSObject class]);
4、创建一个实例对象,实际上分配了多少内存?
#import <malloc/malloc.h> malloc_size((__bridge const void *)obj);
4、函数区别
#import <malloc/malloc.h> // 获取一个类型有多大 size_of(int); // 获取这个类有多大 class_getInstanceSize([NSObject class]); // 分析: // size_of:本质是一个运算符(编译中执行), // class_getInstanceSize:本质是一个函数(运行中执行)。
5、LLDB常用指令
5.1、print、p:打印
5.2、po:打印对象
5.3、读取内存:
// 格式: memory read/数量格式字节数 内存地址 // 简写 x/数量格式字节数 内存地址 //实例1: x/3xw 0x10010 //实例2: x 0x10010
说明:
1、数量:打印的数量
2、格式:x是16进制,f是浮点,d是10进制
3、字节数:
b:byte 1字节,h:half word 2字节
w:word 4字节,g:giant word 8字节
5.4、修改内存中的值:
memory write 内存地址 数值
memory write 0x0000010 10
Demo