OC对象本质

一个NSObject对象占用多少内存

例如,定义如下代码

1
NSObject *obj = [[NSObject alloc] init];

使用 clang -rewrite-objc filename.m -o filename.cpp

使用 xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.c -o main-arm64.cpp

将以上的代码转为 C++ 文件,找到定义 NSObject 结构体

1
2
3
struct NSObject_IMPL { 
Class isa;
};

找到 Class 的定义

1
typedef struct objc_class *Class;

为一个结构体指针,而结构体指针在 Mac 64bit 环境中占用 8 个字节,所以 NSObject_IMPL 结构体只占用了 8 个字节

为了验证此结构体是否只申请了个 8 个字节,使用 malloc_size((__bridge const void *)(obj)) 函数来确定

但此时的返回值却为 16 个字节,为了进一步验证申请的内存大小,可以使用内存进行探究

找到 obj 的内存地址为 0x100709980 ,打开其地址所指向的位置

似乎验证了NSObject 的成员所占的空间为 8 个字节,但申请的内存为 16 个字节,多出的 8 个字节暂时未被利用

但以上的推理仅仅是猜想的验证,为了更加具有说服力,打开 Obj-C 源码进行探究

首先找到 NSObject.mm 文件,方法的索引依次为:

_objc_rootAllocWithZone -> class_createInstance -> _class_createInstanceFromZone -> instanceSize

打开 instanceSize 方法

1
2
3
4
5
6
size_t instanceSize(size_t extraBytes) {
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
}

可以看到,当所申请的的字节数小于 16 时,系统自动会返回 16 个字节,因此得以验证

但如果结构体的大小超过了 16 字节系统将如何分配?

OC 的内存分配

例如下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#import <malloc/malloc.h>
#import <objc/runtime.h>

@interface Student : NSObject
{
@public
int num;
}
@end

int main()
{
Student * stu = [[Student alloc] init];
stu->num = 11;
printf("%ld\r\n", malloc_size((__bridge const void *)(stu))); //输出 16
printf("%ld\r\n", class_getInstanceSize([Student class])); //输出 16
return 0;
}

增加结构体成员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#import <malloc/malloc.h>
#import <objc/runtime.h>

@interface Student : NSObject
{
@public
char name[3];
int num;
int grade;
}
@end

int main()
{
Student * stu = [[Student alloc] init];
memcpy(stu->name, "ccc", 3);
stu->num = 11;
stu->grade = 12;
printf("%ld\r\n", malloc_size((__bridge const void *)(stu))); //输出 32
printf("%ld\r\n", class_getInstanceSize([Student class])); //输出 24
return 0;
}

如果修改结构体中的成员大小

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#import <malloc/malloc.h>
#import <objc/runtime.h>
@interface Student : NSObject
{
@public
char name[20];
int num;
int grade;
}
@end

int main()
{
printf("%ld\r\n", malloc_size((__bridge const void *)(stu))); //输出 48
printf("%ld\r\n", class_getInstanceSize([Student class])); //输出 40
}

第一段代码中,结构体仅需要 16 个字节,系统分配了 16 个字节

第一段代码中,结构体仅需要 24 个字节,系统分配了 32 个字节

第二段代码中,结构体仅需要 40 个字节,系统分配了 48 个字节

所以 OC 的内存分配会以 16 的倍数进行分配

且结构体成员占用的位置和 Windows 相同,但实际占用也就是通过 class_getInstanceSize() 返回的数值为最大的结构体成员大小的倍数,多余的空间使用 0 补齐

另外,根据推论 Xcode 默认使用 4 字节对齐