在上一篇文章 juejin.cn/post/697477… 中我们验证了一个NSObject对象占用16个字节的内存。那么我们这次来验证,自定义类占用多少内存?
-
首先我们创建一个Student类,并创建一个Student对象
@interface Stuednt : NSObject{ @public int _no; int _age; } @end @implementation Stuednt @end int main(int argc, const char * argv[]) { @autoreleasepool { Stuednt *stu = [[Stuednt alloc] init]; } return 0; } -
将OC代码转换成C++源码进行分析:
执行命令:
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp得到C++源码如下:
struct Stuednt_IMPL { struct NSObject_IMPL NSObject_IVARS; int _no; int _age; }; /* @end */ // @implementation Stuednt // @end int main(int argc, const char * argv[]) { /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; Stuednt *stu = ((Stuednt *(*)(id, SEL))(void *)objc_msgSend)((id)((Stuednt *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Stuednt"), sel_registerName("alloc")), sel_registerName("init")); } return 0; } -
查看
NSObject_IMPL结构体,其结构如下:struct NSObject_IMPL { Class isa; };所以我们就可以把
Stuednt_IMPL的结构理解为:struct Stuednt_IMPL { Class isa; int _no; int _age; };整个转换过程,如下图所示:
-
Student内存布局如下:
-
我们知道
指针占内存大小为8个字节,int占内存大小为4个字节,所以我们可以推断出Stuednt对象的成员变量占用的内存大小为:8+4+4 =16个字节。 -
在编译器中进行验证:
Stuednt *stu = [[Stuednt alloc] init]; NSLog(@"%zd", class_getInstanceSize([Stuednt class])); NSLog(@"%zd", malloc_size((__bridge const void *)(stu)));得到结果:
1616所以我们得出了结论:Student对象的内存占用大小为
16字节 -
接下来我们再验证一下,Person对象的内存占用大小为多少?
@interface Person : NSObject{ @public int _no; int _age; int _name; } @end直接在编译器中进行验证:
Person *per = [[Person alloc] init]; NSLog(@"%zd", class_getInstanceSize([Person class])); NSLog(@"%zd", malloc_size((__bridge const void *)(per)));得到结果:
2432按照我们的推断来计算,8+4+4+4 =
24个字节,那么为什么会占用32个字节呢? -
原因:这是因为系统内存对齐的原因导致的,执行
alloc分配内存空间时,操作系统为了方便、快速的管理内存,在分配堆空间时,是一块一块的分配的,而不是一个字节一个字节的分配,这样操作系统对其的操作速度会最快。而在iOS系统中,每块内存都是16的倍数。所以当需要24个字节大小的内存空间时,操作系统就会分配32个字节。- 相关线索:在苹果的libmalloc源码中,可以找到对应宏的注释:每块内存为16的倍数,最大为256:
-
最后总结一下这两个获取内存占用大小的方法:
class_getInstanceSize:创建一个实例对象,至少需要多少内存?malloc_size:创建一个实例对象,实际上分配了多少内存?
-
补充:如果想查看其他操作系统的内存对齐方式,可以在
GNU官方网站中下载对应库的源码:www.gnu.org/software/li…