如题所示
一个对象至少占几个字节呢?
我们分别使用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;
}
打印结果为:
2019-04-01 15:33:26.457385+0800 01-内存分析[5294:290097] obj_sizeof - 8
2019-04-01 15:33:26.457619+0800 01-内存分析[5294:290097] obj_class_getInstanceSize - 8
2019-04-01 15:33:26.457651+0800 01-内存分析[5294:290097] 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
点击alignedInstanceSize()可以看到:
返回的是一个类Class的一个成员变量ivar的大小。
因此,可以看出class_getInstanceSize返回的是一个类中成员变量所占的大小。
malloc_size
点击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个字节虽然分配了,但是并没有使用。