一.首先我们来认识两个方法
class_getInstanceSize
需要导入#import <objc/runtime.h>
此方法获取的是对象实际占用的内存大小(经过内存对齐以后的)
malloc_size
需要导入#import <malloc/malloc.h>
此方法获取的是系统给分配的内存大小
二.我们利用这两个方法来打印一下一个NSObject的内存大小
NSObject *obj = [[NSObject alloc] init];
NSLog(@"%zd", class_getInstanceSize([NSObject class]));
获取obj指针所指向内存的大小
NSLog(@"%zd", malloc_size((__bridge const void *)obj));
得到的结果是8和16,为什么是这个结果呢,接下来我们通过源码来分析一下
三.源码分析
首先把我们的OC代码转换成C++代码,(通过命令行xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc oc源文件 -o 输出的文件)
在cpp文件里可以看到NSObject是一个如下的结构体
struct NSObject_IMPL {
Class isa;
};
Class是一个指针类型,我们知道指针在64位架构里是占8个字节,那为什么系统给分配了16个字节呢,接下来我们看一下NSObject源码,系统分配内存走的是下面的方法
_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone)
{
id obj;
(void)zone;
obj = class_createInstance(cls, 0);
if (slowpath(!obj)) obj = callBadAllocHandler(cls);
return obj;
}
class_createInstance(Class cls, size_t extraBytes)
{
return _class_createInstanceFromZone(cls, extraBytes, **nil**);
}
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
bool cxxConstruct = true,
size_t *outAllocatedSize = nil)
{
......
size_t size = cls->instanceSize(extraBytes);
......
}
size_t instanceSize(size_t extraBytes) {
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
}
uint32_t alignedInstanceSize() {
return word_align(unalignedInstanceSize());
}
这里我们可以看到两个很重要的信息,第一个是如果对象的内存不够16个字节自动补齐为16个字节,第二个是字节对齐,在我们64位架构里是必须为8的倍数,这也就能回答刚刚的问题,接下来我们再来看一个很有趣的东西
@interface Student : NSObject
{
int _no;
NSString *name;
int _height;
}
@end
四.复杂情况
我们创建一个Student类型的对象,那占用多少内存呢,系统又分配多少内存呢,首先我们来转一下C++代码看一下Student的内存结构
struct Student_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int _no;
NSString *name;
int _height;
};
有的小伙伴可能就开始算了,8(NSObject_IVARS)+4(_no)+4(_height)+8(name)=24,占了24个字节的内存(符合内存对齐原则为8的倍数),实际分配为32字节(实际分配必须是16的倍数),那到底是不是呢,接下来我们打印一下
打印结果是32,32,那为什么是这个结果呢,大家不要忘了内存对齐,如果剩余的内存放不下的话是会开辟新的内存的,就拿这个例子举例,首先是isa指针占8个字节,_no分配8个,实际占4个,接下来是name指针需要8个字节,但是只剩余4个,不够所以会再开辟8个字节,_height占8个,最后结果是8+8+8+8=32个字节,那如果把name和_height的顺序换一下呢
@interface Student : NSObject
{
int _no;
int _height;
NSString *name;
}
@end
此时结果会完全不一样,首先是isa指针占8个字节,_no分配8个占4个,_height需要4个字节正好还剩下4个,所以把之前剩余的空间占满,_name分配8个字节,所以系统分配了8+8+8=24个字节
相信通过这个例子,大家能够更清晰的认识到了对象的内存分配了。