问题:NSObject对象占用内存空间大小为多少字节?
补充: 一个字节由8个二进制位组成.
示例代码如下所示.
int main(int arg1, const char* argv[]) {
NSObject *obj = [[NSObject alloc] init];
return 0;
}
转义为Arm64位的源码指令如下所示.
xcrun -sdk iphoneos -arch arm64 -rewrite-objc 源文件路径 -o 目标文件路径
iOS对象的本质是C/C++的结构体,通过转义或者查看我们得知iOS对象结构体中有且只有一个成员变量,那就是isa指针.所以isa指针内存地址即为iOS对象结构体的地址.
struct NSObject_IMPL {
Class isa;
};
那么一个NSObject对象到底占用了多少内存呢.
我们通过引入 <objc/runtime.h>
使用如下方法发现,字节数是 8个字节.
class_getInstanceSize([NSObject class]);
但是我们通过引入 <malloc/malloc.h>
使用如下方法,字节数竟然是 16个字节.
malloc_size(const void *ptr);
// 使用方式如下
malloc_size((__bridge const void *)obj);
这时候我们就可以去官方查看一下具体的源码,然后下载最新的objc4中的源码了,开源源码地址如下所示.
结论
通过阅读代码发现, class_getInstanceSize 是返回成员变量的大小,并非结构体本身的大小,NSObject成员变量有且只有一个isa指针,所以只有8个字节.
size_t class_getInstanceSize(Class cls) {
if (!cls) return 0;
ruturn cls->alignedInstanceSize();
}
// Class's ivar size rounded up to a pointer-size boundary.
uint32_t alignedInstanceSize() {
ruturn word_align(unalignedInstanceSize());
}
那么为什么NSObject对象创建时会有16个字节呢? 调用流程如下所示.
PS: 基于 objc4-818.2.tar.gz
版本
id _objc_rootAllocWithZone(Class cls, malloc_zone_t *zone) {
id obj;
if (fastpath(!zone)) {
obj = class_createInstance(cls, 0);
} else {
obj = class_createInstanceFromZone(cls, 0, zone);
}
if (slowpath(!obj)) obj = _objc_callBadAllocHandler(cls);
return obj;
}
id (* zoneAlloc)(Class, size_t, void *) = _class_createInstanceFromZone;
id class_createInstanceFromZone(Class cls, size_t extraBytes, void *z) {
OBJC_WARN_DEPRECATED;
return (*zoneAlloc)(cls, extraBytes, z);
}
id _class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone) {
void *bytes;
size_t size;
// Can't create something for nothing
if (!cls) return nil;
// Allocate and initialize
size = cls->alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
if (zone) {
bytes = malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
} else {
bytes = calloc(1, size);
}
return objc_constructInstance(cls, bytes);
}
通过 _class_createInstanceFromZone 方法,我们可以明确了解当 size < 16 时,内存分配大小为16个字节,而size的计算是通过 alignedInstanceSize 获取成员变量大小 再加上 外部extraBytes的大小而生成的,NSObject成员变量有且只有一个isa指针为8个字节,所以计算出来的size大小为8字节(小于16字节),因此最终的字节数为16字节.
所以一个NSObject对象占用的内容大小为16个字节.
问题: 一个普通类创建时需要开辟多少内存空间呢?
操作系统 | 数据类型 | 字节数 |
---|---|---|
32位 | char | 1个字节 |
64位 | char | 1个字节 |
32位 | char *p (指针) | 4个字节 |
64位 | char *p (指针) | 8个字节 |
32位 | int | 4个字节 |
64位 | int | 4个字节 |
32位 | unsigned int | 4个字节 |
64位 | unsigned int | 4个字节 |
32位 | double | 8个字节 |
64位 | double | 8个字节 |
32位 | long | 4个字节 |
64位 | long | 8个字节 |
32位 | long long | 8个字节 |
64位 | long long | 8个字节 |
32位 | BOOL | 1个字节 |
64位 | BOOL | 1个字节 |
32位 | float | 4个字节 |
64位 | float | 4个字节 |
32位 | NSInteger(=int) | 4个字节 |
64位 | NSInteger(=long) | 8个字节 |
32位 | CGFloat(=float) | 4个字节 |
64位 | CGFloat(=double) | 8个字节 |
32位 | CGSize | 8个字节 |
64位 | CGSize | 16个字节 |
32位 | CGRect | 16个字节 |
64位 | CGRect | 32个字节 |
32位 | short | 2个字节 |
32位 | unsigned long | 4个字节 |
在不同的位操作系统下,只有指针数据类型所占用空间是不同的,所以一个普通类的所占用的内存空间就是 isa指针 + 所有成员变量的所占用的内存空间.然后根据内存对齐法则,即可算出成员变量所占用的内存空间大小.
结构体内存对齐法则: 结构体的开辟内存大小必须是最大成员所占字节的倍数.
然后,调用开辟内存方法 calloc 如果总内存大小满足 16字节(iOS系统最小开辟空间大小规范) 的倍数,那么就直接开辟,否则就需要按需新增内存空间.这有点类似于结构体内存对齐原则.
在 libmalloc 源码可以查询到开辟空间规范限制,如下所示.
#define NANO_MAX_SIZE 256 /* Buckets sized {16, 32, 48, ...., 256}*/
例如 如果一个继承于NSObject的类有且只有一个CGRect属性,由于CGRect是结构体,最终由 4个 CGFloat类型构成,那么就是32个字节,再加上isa指针的8个字节,由于都是8字节数据类型,进行内存对齐法则之后,依然是8个字节,故最终占用40个字节,然后还要根据是16字节的倍数,所以这个对象最终开辟48个字节.
Comments | 0 条评论