本人2016年进入iOS开发行业,时间一晃也工作了三四年了,写过不少项目,代码写的也有几万行了,有的时候就会陷入思考,当我们写完代码编译完成后怎么就运行到手机上成了一个App,这中间到底经历了什么过程?今天就把自己这几年对于OC对象的了解分享一下,如有错误欢迎指正。
首先说明一下,为什么要了解和研究OC对象的本质??
- 了解了对象的本质,也可以写出更加优秀,内存占用更合理的代码,提高系统的性能。
- 了解了本质,才可以在开发中游刃有余的玩转各种黑科技,处理各种看似不可能完成的需求。
- 在面试中脱颖而出
查阅了很多资料最后得出此图
1577366532813.jpg搞过iOS的同学对此并不陌生,大家都知道OC会编译成C++,那么OC在内存中是怎么存储的?是以什么方式存储的呢?今天就来看看。
- 新建一个工程写这么一行代码
NSObject *obj = [[NSObject alloc] init];
- 按住command点进去可以看到苹果官方对NSObject的声明
@interface NSObject <NSObject> {
Class isa OBJC_ISA_AVAILABILITY;
}
- 将OC转化成C++
具体的怎么转化请移步简书OC转化C++ - 拿到转化后的C++代码,我们发现NSObject在C++中成了这个
//NSObject底层实现
struct NSObject_IMPL {
Class isa;
};
struct what?这不是结构体吗?对,是的,OC对象转化成C++代码后就是通过结构体的形式存储在内存中的。一个对象可能多个不同类型的属性,所以以结构体的形式存储。
通过上面的探索我们得出OC对象在编译的过程中转化成了C++代码,并且以结构体的形式在内存中存储。并且由一个指针指向这块内存空间,那么问题来了一个NSObject对象在没有成员自定义成员变量的情况下在内存中占了多大的内存空间。
//NSObject底层实现
struct NSObject_IMPL {
Class isa;
};
这行代码中的isa是个指针,那么一个指针在64bit环境下占8个字节,在32bit环境下占4个字节。那个NSObject只拥有一个Class isa成员变量是不是占有8个字节呢?
验证
// 获得NSObject实例对象的成员变量所占用的大小 >> 8
NSLog(@"%zd", class_getInstanceSize([NSObject class]));
输出 8
// 获得obj指针所指向内存的大小 >> 16
NSLog(@"%zd", malloc_size((__bridge const void *)obj));
输出 16
一个 NSObject 对象在创建后系统分配了16个字节,只是自己的成员变量占用了8个字节
或者可以说成
系统分配了16个字节给NSObject对象(通过malloc_size函数获得) 但NSObject对象内部只使用了8个字节的空间(64bit环境下,可以通过class_getInstanceSize函数获得)
原因:苹果的规定吧,通过malloc_size方法的底层实现我们可以看到,当系统对一个对象分配的内存<16字节时,系统会强制分配16字节,也就是说对于一个对象系统最少分配16字节。
进一步验证
- 创建一个Student类,写两个成员变量
@interface Student : NSObject
{
@public
int _no;
int _age;
}
@end
@implementation Student
@end
- 拿到C++代码,我们发现长成了这个样子
struct Student_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int _no;
int _age;
};
通过查找发现NSObject_IMPL 是这个样子
struct NSObject_IMPL {
Class isa;
};
汇总: Student对象转化成C++后最终结果
struct Student_IMPL {
Class isa;
int _no;
int _age;
};
但我们在执行这段代码的时候
Student *stu = [[Student alloc] init];
stu->_no = 4;
stu->_age = 5;
NSLog(@"%zd", class_getInstanceSize([Student class]));
NSLog(@"%zd", malloc_size((__bridge const void *)stu));
实际是stu 指针指向了struct Student_IMPL这个结构体所对应的内存空间,这个结构体的内存空间地址,就是该结构体第一个元素的内存地址 Class isa;也就是isa指针指向的地址
- 验证
struct Student_IMPL *stuImpl = (__bridge struct Student_IMPL *)stu;
NSLog(@"no is %d, age is %d", stuImpl->_no, stuImpl->_age);
输出 4 5
- 那个这个Student对象占据内存空间大小?系统给该对象分配内存大小呢?
分析: 一个指针占8个字节,一个int类型占4个字节,两个那就是8个字节,合计16个字节
NSLog(@"%zd", class_getInstanceSize([Student class]));
输出 16
NSLog(@"%zd", malloc_size((__bridge const void *)stu));
输出 16
NSObject对象和Student对象所对应的内存图
Student
思考
- 创建一个Person类,写一个成员变量
@interface Person : NSObject
{
@public
int _no;
}
@end
@implementation Person
@end
- 再创建一个Student类继承与Person类,写一个成员变量
@interface Student : Person
{
@public
int _age;
}
@end
@implementation Student
@end
那么Person 和 Student 分别占用多大的内存呢?
答案:16,16
分析:通过上面的验证可以知道Person 转化成C++
struct Person_IMPL {
struct NSObject_IMPL NSObject_IVARS; //8字节
int _no; //四字节
};
那么这两个字节相加应该是12字节?为什么是16字节呢,因为上面提到过,苹果为一个实例对象开辟的最小内存空间为16字节(这是规定),还有就是内存对齐原则,分配的总内存数是最大成员变量的整数倍,其中最大的成员变量是Class isa指针,8字节,整数倍也就是16字节
分析:通过上面的验证也可以知道Student 转化成C++
struct Student_IMPL {
struct Person_IMPL Person_IVARS; //8字节
int _no; //四字节
int _age//四字节
};
三个成员变量相加正好16字节,又是8的整数倍,所以分配16字节
图示:
关系图.jpg写了这么多只写到了成员变量那么属性呢?请看以下代码
@interface Student : NSObject
{
@public
int _age;
}
@property (copy,assign) int weight;
@end
@implementation Student
@end
那么创建一个这样的Student对象占多大的内存呢?
答案: 16个 属性weight 会自动生成_weight 成员变量,当然也还有get和set方法,那么两个成员变量8字节,一个指针8字节,相加16字节。
总结:一个实例对象在创建时至少分配16字节,每个对象都会有一个isa指针,再加上成员变量所占空间就是分配的总空间。
补充:指针类型成员变量如NSString *str, double number, long height 占8字节在64bit机器下,int float 占4字节,OC对象分配内存遵循内存对齐法则。
苹果内存对齐法则:分配的总内存数是最大成员变量的整数倍
那么真的是这样吗?还有什么其他的吗?
下面一探究竟:
- 创建一个Person对象
@interface Person : NSObject
{
int _age;
int _height;
int _no;
}
@end
@implementation Person
@end
- 执行代码
MJPerson *p = [[MJPerson alloc] init];
NSLog(@"%zd %zd",
class_getInstanceSize([MJPerson class]), // 24
malloc_size((__bridge const void *)(p))); // 32
输出 24 32
- 分析:
一个Person对象,有_age,_height,_no,isa,这四个成员变量三个int类型,12字节,一个isa 指针8字节,总共20字节。很容易理解内存对齐 法则:成员变量所占内存最大成员变量的整数倍,所以占用了24字节。malloc_size 这个方法返回32字节,说明操作系统实际为这个对象分配了32字节,这个为什么?
通过查看源码我们发现:
1578315925798.jpg
操作系统为对象分配的内存空间都是16的整数倍,所以也就有了malloc_size 这个方法返回32字节了
网友评论