1.2.1 对象内存大小函数分析 - 476139183/Learning-iOS GitHub Wiki
分析 sizeof、class_getInstanceSize、malloc_size 函数的区别
在分析一个对象至少占几个字节的时候,
我们分别使用 sizeof、class_getInstanceSize、malloc_size三个函数方法进行操作:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <malloc/malloc.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSObject *obj = [[NSObject alloc] init];
NSLog(@"obj_sizeof - %d", sizeof(obj));
NSLog(@"obj_class_getInstanceSize - %d", class_getInstanceSize([NSObject class]));
NSLog(@"obj_malloc_size - %d", malloc_size((__bridge const void *)(obj)));
}
return 0;
}
打印结果为:
obj_sizeof - 8
obj_class_getInstanceSize - 8
obj_malloc_size - 16
可以看出,sizeof、class_getInstanceSize返回的 obj 占8个字节,malloc_size 返回obj占16个字节。
那么,为什么打印出结果不一样呢?它们都是什么意思呢?
首先,sizeof(A) 不是函数,而是编译器特性,是一个运算符,在编译期间就可以确定A类型所占的字节大小。由于我们放入的是 obj 对象,而 obj 是一个指向对象的指针,因此,sizeof 返回的8是obj指针所占的字节大小。而如果放入的是 [NSObject class],返回什么呢?为什么呢?
class_getInstanceSize 的底层究竟是什么呢? 点击进去在 runtime.h 文件中发现 class_getInstanceSize 的定义为:

好吧,好像什么也看不出来,那么只能通过底层开源代码查看 class_getInstanceSize 的实现方式了。那么
如何获取苹果底层开源代码呢?
可以通过苹果开源代码,点击 objc4 文件夹,下载最新的 objc4-xxx.tar.gz,解压后打开项目,在项目里面搜索class_getInstanceSize,可以在 objc-class.mm 文件中看到:

点击 alignedInstanceSize() 可以看到:

返回的是一个类Class的一个成员变量ivar的大小。 因此,可以看出
class_getInstanceSize返回的是一个类中成员变量所占的大小。
点击malloc_size,进入可以看到如下代码:

malloc_size通过输入一个指针ptr,可以返回指针所指向的内容的大小,malloc_size返回结果是一个对象实际分配的空间大小。
由此可见:
一个对象obj分配了16个字节,通过《iOS中类、对象的本质》可知,一个对象obj中包含一个Class类型的指针isa,而根据《一个指针占几个字节?原理是什么呢?》可知,一个指针占8个字节(在64位电脑上),也就是Class类型的isa指针所占的8个字节大小。
那么,为什么一个对象分配了16个字节,对象只用了8个字节呢?
我们知道 NSObject *obj = [[NSObject alloc] init]; 中alloc其实是调用的是 + (instancetype)allocWithZone:(struct _NSZone *)zone; 方法,而通过上面我们下周的底层开源代码可以发现 allocWithZone 调用的是:

继续点击,可以发现 _objc_rootAllocWithZone 的实现:

在这里面有一个 class_createInstance 函数,点击进入:

再次点击 _class_createInstanceFromZone 进入:

里面有一个 obj = (id)calloc(1, size); 函数,是创建obj的,而size是由 size_t size = cls->instanceSize(extraBytes); 返回的。点击 instanceSize 进入可以发现:

这里面规定了 if (size < 16) size = 16; 即,如果分配的size小于16,那么就让 size=16 。也就是,一个对象至少分配16个字节。
另外,我们也可以通过查看obj的内存地址,通过内存地址找到所占用的二进制,查看obj到底是占用几个字节
通过打断点,我们可以找到obj的内存地址 0x102800930,在Debug-Debug Workflow -View Memory

把内存 0x102800930 填写在下列框中:

可以看到其二进制内容: 41 61 88 9C FF FF 1D 00 00 00 00 00 00 00 00 00 该二进制是用16进制表示的,也就是一个数字是4个2进制,两个数字整好是8个二进制,也就是一个字节。通过观察可以看出,该二进制共有16个字节,但是,只有8个字节有内容,存放obj的内容,其余8个字节虽然分配了,但是并没有使用。