上一节(CH1-Q1)我们谈到一个普通的NSObject实例对象所占用的内存空间。NSObject对象内部虽然只使用了8个字节(isa指针),但系统分配了16个字节给NSObject 实例对象,因此一个NSObject实例对象实际所占用的内存空间为16个字节。
但在我们日常的开发中,我们不仅仅使用NSObject这个对象,我们还会自定义许多自己业务相关的对象,那么接下来我们一起去了解一下一个自定义的实例对象在内存中又是如何去分布的?它又占用多少的内存空间?
创建一个MacOS命令行程序,声明一个Student类:
@interface Student : NSObject {
@public
int _no;
int _age;
}
@implementation Student
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Student *stu = [[Student alloc] init];
}
return 0;
}
*Q2:一个Student实例对象占用多少内存空间?*
这个问题跟我们上一节问的一个NSObject实例对象占用多少内存空间?其实是类似的,我们也可以把这个问题转换成: stu
指针指向的内存空间占多大?
我们知道每创建一个Student实例对象
,该实例对象都有属于自己的成员变量_no
和_age
,而它们的类型都是int
类型,我们知道一个int
类型的成员变量在64bit机器上所占用的内存空间是4个字节,因此_no
和_age
成员变量一共占用8个字节。那么,是否一个Student实例对象
就只占用8个字节呢?答案是否定的,因为我们可以观察到 Student : NSObject ,Student
这个类是继承于NSObject
这个类的,一个NSObject实例对象
都占用了16位的内存空间,那么身为它子类的实例对象,不可能比NSObject实例对象
所占用的内存空间少。
既然继承NSObject
,那么NSObject
的isa
成员变量应该在Student实例对象
中也存在。那么我们如何去验证呢?我们可以通过终端的方式,生成C++代码去观察。
在终端输入:
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
通过搜索: struct Student_IMPL
,我们能够得到以下的结构体:
struct Student_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int _no;
int _age;
};
可以发现Student_IMPL
结构体中包含一个NSObject_IMPL
结构体的成员变量,对于NSObject_IMPL
我们已经很熟悉了,其实它就是以下的代码:
struct NSObject_IMPL {
Class isa;
};
结合上面的两个结构体,我们能够得出:
struct Student_IMPL {
Class isa; // 8个字节
int _no; // 4个字节
int _age; // 4个字节
};
15454041174968.jpg
我们可以通过:把指向Student实例对象
的指针stu
强制转换成struct Student_IMPL *
结构体类型的指针stuImpl
,同时我们可以使用stuImpl
指针获取_no
和_age
的值,如果输出的值是跟我们再前面给Student实例对象
赋值一样的话,我们可以证明,我们上图的Student实例对象
内存分配是正确的。
struct Student_IMPL {
Class isa; // 8个字节
int _no; // 4个字节
int _age; // 4个字节
};
@interface Student : NSObject
{
@public
int _no; //4个字节
int _age;
}
@end
@implementation Student
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
//stu指针存储的其实就是 isa的地址值
Student *stu = [[Student alloc] init];
stu->_no = 7;
stu->_age = 8;
// 把stu 转成stuImpl类型
struct Student_IMPL *stuImpl = (__bridge struct Student_IMPL *)stu;
NSLog(@"no:%d", stuImpl->_no);
NSLog(@"no:%d", stuImpl->_age);
}
return 0;
}
当然我们也可以用另外一个角度去证明:
15454048403144.jpg
获取Student实例对象
的地址(0x0000000102120390
),我们可以通过(View Memory)这个工具来帮我们一探究竟。
最后我们还可以通过上一节用到<malloc/malloc.h>
中的malloc_size
和<objc/runtime.h>
中的class_getInstanceSize
两个方法去得到Student实例对象
实际分配的内存大小和所用到的内存大小。
可以看到两个方法所输出的值都为16,那么我们可以说该Student类的实例对象占用的内存空间为16个字节,而且它实际使用到的内存空间也为16个字节。
如果我们再在Student类中加入一个double
类型的成员变量height
,那么其结构体就可以转换成:
struct Student_IMPL {
Class isa;
int _no; // 4个字节
int _age; // 4个字节
double _height; // 8个字节
};
15454061417297.jpg
我们可能又有疑问,为什么开辟的内存空间大小是32呢?其实这里就涉及到一个内存对其的概念,通过我的归纳,可以总结为:
-
malloc_size()为类对象动态分配的内存大小,是补齐之后的大小,即如果一个类对象需要的内存大小为20,通过malloc_size()方法获取的大小为32,即16的倍数;
-
class_getInstanceSize()方法获取的内存大小也是补齐之后的大小,补齐规则按照类的成员变量所占内存最大值的整数倍
对照上面的Student类就是:
-
isa
、_no
、_age
、_height
属性一共占用24个字节,但是实际需要分配的大小需要为16的倍数,因此最低要开辟32个字节的内存空间。 -
而
isa
、_no
、_age
、_height
属性一共占用24个字节,是类的成员变量isa
和_height
所占内存的最大值的整数倍,因此Student类的属性使用了24个字节的内存空间。
总结:通过上一节(CH1-Q1)和本节的内容,相信大家对NSObject对象和我们自定义的对象它们的实例对象在内存中占用的内存空间究竟是多少有了一定了了解。另外下一节的内容我们会继续探讨OC对象的本质:包括OC对象的方法又是存在内存哪里的?alloc和size的分析等内容。
如果对本节内容有什么疑问?可以添加下面的微信跟我一起讨论。
1791545122023_.pic.jpg
网友评论