一个OC对象占用多大内存?
解决这个问题,需要探究OC对象的本质。
实际上Object-C最后都会转化成C/C++混编代码,因此利用clang编译器将Object-C转为对应的代码
// main.m
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSObject *objc = [[NSObject alloc] init];
}
return 0;
}
目标:将上述代码转为C/C++代码
具体步骤:
1、打开终端,cd到main.m所在文件夹
2、输入命令
xcrun -skd iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp
/**
* xcrun Xcode提供的工具
* -sdk iphoneos 指定真机环境
* clang 前端编译器命令
* -arch arm64 指定64位编译器
* -rewrite-objc 指定重写objc语言
* main.m 源文件
* -o 输出
* main.cpp 目标文件
*/
说明:
1、此处不直接使用clang转化C/C++语言,是因为没有指定环境,转化出来的文件会比较大,内容比较多。
2、目标文件直接转为.cpp,而不是.c,是因为Object-C实际上由C和C++混合在一起的。如果只转为C语言,会少了很多语法。
在main.cpp中会找到如下结构
// main.cpp
struct NSObject_IMPL { // OC对象的本质
Class isa;
};
typedef struct objc_class *Class; // 实际上是一个指针
解析出来后OC对象实际上包含了一个isa指针。而在64位编译器,一个指针的大小是8个字节。
为了证明这个猜想,引入runtime框架来进行证明
// mian.m
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
/**
* class_getInstanceSize 表示获取一个类生成的实例对象大小
* %zu 输出size_t,size_t是C/C++标准在stddef.h中定义表示对象大小
*/
NSLog(@"%zu", class_getInstanceSize([NSObject class]));
}
return 0;
}
result: 8
结果为8,即8个字节。验证了上述的猜想。
但是实际上真的是8个字节吗?运行一下下方代码。
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <malloc/malloc.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSObject *objc = [[NSObject alloc] init];
NSLog(@"使用大小:%zu", class_getInstanceSize([objc class]));
NSLog(@"实际分配大小:%zu", malloc_size((__bridge void *)objc));
}
return 0;
}
result:
使用大小:8
实际分配大小:16
实际上class_getInstanceSize函数得到的是对象真正使用的内存大小,而malloc_size函数得到的是系统给对象分配的内存大小。
总结如下
问:
一个OC对象占用多大内存?
答:
实际上分配了16个字节的存储空间给NSObject对象,真正只用了一个指针变量占用的大小(64位下占8个字节,32位下占4个字节)
【这样才比较严谨】
衍生题
问题:求Person和Student分别占据的内存大小
@interface Person : NSObject {
int _age;
}
@end
@implementation Person
@end
@interface Student : Person {
int _num;
}
@end
@implementation Student
@end
解决这个问题,利用上述的转化过程,将下面的main.m转化得到一个新的main.cpp
// main.m
#import <Foundation/Foundation.h>
@interface Person : NSObject {
int _age;
}
@end
@implementation Person
@end
@interface Student : Person {
int _num;
}
@end
@implementation Student
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
}
return 0;
}
提取main.cpp中的有用信息,得到如下结果
struct NSObject_IMPL {
Class isa;
};
struct Person_IMPL {
struct NSObject_IMPL NSObject_IVARS; // 实际上替换为NSObject_IMPL,一个isa
int _age;
};
struct Student_IMPL {
struct Person_IMPL Person_IVARS; // 实际上为替换为Person_IMPL,一个isa指针,一个int
int _num;
};
前提:64位环境下,指针变量占8个字节,int变量占4个字节。
1、Person由一个指针变量isa,和一个int变量_age组成。因此, 8+4=12?
实际上为8+(4+4空白)=16个字节,由于内存对齐的原因,因此有4个空白字节进行了填充。
2、Student由一个指针变量isa、一个int变量_age,一个int变量_num。此时原先填充的4个空白,被_num占据了。Student对象此时为8+4+4 = 16字节。
为了验证猜想,利用上述的class_getInstanceSize进行验证。
// maim.m
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface Person : NSObject {
int _age;
}
@end
@implementation Person
@end
@interface Student : Person {
int _num;
}
@end
@implementation Student
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"%zu", class_getInstanceSize([Person class]));
NSLog(@"%zu", class_getInstanceSize([Student class]));
}
return 0;
}
result: 16 16
结果为16和16,即都为16个字节。验证了上述的猜想。
作答:
Person占16个字节,Student占16个字节(64位环境下)
网友评论