张东轩的博客

合抱之木,生于毫末;九层之台,起于累土;千里之行,始于足下。

0%

NSObject对象占用多少内存

NSObject的本质

1
2
3
4
5
6
7
8
9
10
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {

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

NSLog(@"Hello, World!");
}
return 0;
}

以上一段代码使用如下一段命令进行rewrite后,会被改为c++代码,生成main.cpp

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

1
2
3
4
5
6
7
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
NSObject *obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_s0_4wsgyhps0fv7r90xwgjm4lwr0000gn_T_main_96c2d6_mi_0);
}
return 0;
}

NSObject 的结构为

1
2
3
4
5
typedef struct objc_class *Class;
struct objc_object {
Class _Nonnull isa __attribute__((deprecated));
};
typedef struct objc_object NSObject;

可以看到NSObject是由结构体objc_object来定义的,objc_object里面只有一个Class类型的isa属性,指向该类的类信息;Class是由结构体 objc_class来定义。由此可以知道oc编写的代码都会被翻译为C++代码。

NSObject的内存占用

class_getInstanceSize是一个runtime提供的API,用于获取类实例对象所占用的内存大小,源码如下:

1
2
3
4
5
6
7
8
9
10
size_t class_getInstanceSize(Class cls)
{
if (!cls) return 0;
return cls->alignedInstanceSize();
}

// Class's ivar size rounded up to a pointer-size boundary.
uint32_t alignedInstanceSize() const {
return word_align(unalignedInstanceSize());
}

根据源码的意思来看, class_getInstanceSize 就是获取实例对象中成员变量内存大小。

下面我们使用一个case来说明一个对象占用内存的大小:

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

int main(int argc, const char * argv[]) {
@autoreleasepool {

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

// 获得NSObject实例对象的成员变量所占用的大小
size_t class_size = class_getInstanceSize(NSObject.class);

// 获得ptr指针所指向内存的大小
void *ptr = (__bridge void *)obj;
size_t ptrmalloc_size = malloc_size(ptr);

NSLog(@"class_size:%zu ptrmalloc_size:%zu", class_size, ptrmalloc_size);
}
return 0;
}

运行结果为

class_size:8 ptrmalloc_size:16

运行结果说明nsobject类对象的大小正好是isa指针的大小为8个字节,使用view memory可以查看ptr指向的内存,占用16个字节。
这是因为在objc的alloc内存申请的流程中有这样一段代码,限制一个NSObect对象的大小至少要占用16个字节。

1
2
3
4
5
6
7
8
9
10
size_t instanceSize(size_t extraBytes) const {
if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
return cache.fastInstanceSize(extraBytes);
}

size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
}

所以我们知道NSObject中只有一个isa指针,所以NSObject类对象占用8个字节,NSObject在内存中占用16个字节。

自定义类的内存占用

定义Test类,并进行如下初始化,那么obj在内存中是怎样布局的呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@interface Test : NSObject

@property(nonatomic, assign) int property_1;
@property(nonatomic, assign) int property_2;

@end

@implementation Test
@end

int main(int argc, const char * argv[]) {
@autoreleasepool {

Test *obj = [[Test alloc] init];
obj.property_1 = 1;
obj.property_2 = 2;

// 获得NSObject实例对象的成员变量所占用的大小
size_t class_size = class_getInstanceSize(Test.class);

// 获得ptr指针所指向内存的大小
void *ptr = (__bridge void *)obj;
size_t ptrmalloc_size = malloc_size(ptr);

NSLog(@"class_size:%zu ptrmalloc_size:%zu", class_size, ptrmalloc_size);
}
return 0;
}

运行的结果如下

class_size:16 ptrmalloc_size:16

使用view memory查看ptr指向的内存,如下图

其中红框是对象属性property_1和property_2 的值,分别是1和2,int类型各占4个字节。

如果Test的定义如下

1
2
3
4
5
6
7
@interface Test : NSObject

@property(nonatomic, assign) int property_1;
@property(nonatomic, assign) int property_2;
@property(nonatomic, assign) int property_3;

@end

初始化obj并赋值

1
2
3
4
Test *obj = [[Test alloc] init];
obj.property_1 = 1;
obj.property_2 = 2;
obj.property_3 = 3;

运行结果为

class_size:24 ptrmalloc_size:32

则在内存中的布局如下,可以看到property_1、property_2和property_3 的值分别为1、2、3

首先Test的类对象的大小为24,但是为何不是 8(isa指针) + 三个属性占用的12个字节 = 20 个字节?
这是因为表示Class的struct objc_class是一个结构体,结构体大小结果要为成员中最大字节的整数倍,所以Test类指向的objc_class变量大小为24。

对象在内存中占用了32个字节,这是因为malloc要满足 16 字节对齐原则 ( 可以在 libmaclloc 源码查找到 ) , 因此实际总占用内存为24. 而实际开辟则满足对齐标准开辟为 32.

参考
https://juejin.cn/post/6844903939985391629
https://zhuanlan.zhihu.com/p/98432137