一个NSObject对象占用多少内存

问题提出:

\color{#FF0000}{一个NSObject对象占用多少内存?(答案在最下面)}

解析过程

要知道NSObject对象占用多少内存,需要知道创建一个NSObject对象的时候都做了什么。
比如下面这行代码:

NSObject *object = [[NSObject alloc] init];

首先我们点进去看看NSObject是怎么定义的

@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
    Class isa  OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}

然后发现NSObject的声明中只有一个class 类型的isa指针成员变量,按住command键看一下class又是个什么东西呢。点进去我们发现,class是一个指向结构体的指针,具体是什么这个结构体是什么我们先不管。

typedef struct objc_class *Class;

另外我们知道平时编写的Objective-C代码底层实现其实是C/C++代码,然后转成汇编语言,最后生成机器阅读的机器语言也就是01010110这种。
也就是:


既然这样,那我们就看看NSObject 转成C/C++代码是什么样的。
通过下面任何一种方式都可以将OC语言转成C/C++

方式一:clang -rewrite-objc main.m(main.m是要被转C/C++的文件名字) -o main.cpp(main.cpp是要生成C/C++的文件名字)
方式二:xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m(这里是要被转C/C++的文件名字) -o main-arm64.cpp(main-arm64.cpp是要生成C/C++的文件名字)

方式一是将OC直接转成C/C++ 语言,(注:-o main.cpp 这个是要生成C/C++的文件名,可写可不写)
方式二是将OC转成适应于 64位真机下的 C/C++ 语言,-o main-arm64.cpp这行可写可不写,默认生成<原类名.cpp>文件

在此我们也举一个简单的例子,直接创建一个macOS项目就好(等下后面运行不需启动模拟器)。

然后在main.m文件的main函数里简单写一行代码

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        NSObject *obejct = [[NSObject alloc] init];
        
    }
    return 0;
}

接着打开终端。cd到文件目录下,然后选择上面任何一种转成C/C++的方式,这里我用方式二
即:
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp

然后就会生成一个 main-arm64.cpp 文件,我们打开看一下。
直接搜索NSObject,就会发现有这么一个结构体

struct NSObject_IMPL {
    Class isa;
};

此处我们也可以得之,OC的类在C/C++中实现的方式就是一个结构体,即OC类是以结构体的形式在内存中存在的。而且NSObject内部只有一个class 类型的isa指针,既然是一个指针,那么我们知道,指针在64环境下就是占8个字节(在32位环境下就是4个字节。)既然这个指针占用8个字节,而且NSObject这个结构体内部只有这么一个结构体,很显然,这个结构体也就是占用8个字节。

所以,

NSObject *obejct = [[NSObject alloc] init];

这句代码的过程就是:
[NSObject alloc]在内存中分配地址,然后创建对象。接着将object这个指针指向刚刚创建的对象,最后将分配的对象地址赋值给object指针。也就是


那么这样看来,创建一个NSObject对象具体分配的大小 \color{red}{应该} 就是8个字节,因为前面咱们已经分析出,NSObject底层实现方式其实就是一个结构体,而这个结构体内部只有一个占8个字节的isa指针。到底是不是呢需要验证一下。
runtime里面有这么一个函数,获取的实例大小

class_getInstanceSize(Class _Nullable cls)

既然有这么个方法,那咱们就打印验证一下,看是不是8个字节呢,
注意要先引入
#import <objc/runtime.h>

NSLog(@"NSObject InstanceSize:%zd",class_getInstanceSize([NSObject class]));

结果:NSObject InstanceSize:8

经过测试发现,“果然”是8个字节。有眼尖的小伙伴可能会发现这里可能会有坑。为了确保安全,我们需要看看 class_getInstanceSize(Class _Nullable cls)

这里是OC源码地址
https://opensource.apple.com/tarballs/

打开链接往下翻或直接搜索 objc4,点进去会发现好多版本,然后找到最大的那个,也就是最新的下载、解压。打开解压好的源码,这是不能直接运行的,但是我们可以大略看一下。
打开之后直接搜索 class_getInstanceSize,找.mm文件看实现代码

image.png

我们发现,在调用class_getInstanceSize(Class _Nullable cls)函数的时候,如果传入的类对象为nil,就会直接返回0。否则就调用alignedInstanceSize函数,也就是对齐后的实例大小,接着点击去发现

image.png

调这个方法的意思是:返回unalignedInstanceSize(未对齐的实例大小)对齐后的大小
这也是遵循了内存分配法则之一:内存对齐原则。但这个函数的注释是返回class的成员变量大小,注意,此处可有有蹊跷了。既然这个函数式返回对象的成员变量的大小,那么这个大小是不是就是创建对象时分配的大小呢?为了证明我们也看看 NSObjectalloc方法是怎么实现的。对象调用alloc方法其实就是调用了allocwithzone,我们直接在刚刚下载的OC源码里搜索allocwithzone,找到这个函数的实现,发现在这个函数里调用了一个创建实例对象的函数

接着我们看看这个函数式怎么实现的,点进去发现这里面有调用了一个获取实例对象大小的函数
image.png

然后我们再进一步点进去发现里面计算的size是调用了instanceSize(size_t)这个函数

image.png

我们接着点进去,最后找到这个,也就是实例对象的大小

image.png

我们发现这个函数的有一行注释,意思就是CoreFoundation框架规定所有的对象至少要16个字节。也就是如果这个对象不足16个字节,也会分配给你16个字节。
虽然这么说,我们也得验证一下alloc在创建的时候是不是真的分配了16个字节。
首先我们在之前自己创建的文件里导入
#import <malloc/malloc.h>
然后会查看这个函数

extern size_t malloc_size(const void *ptr);
/* Returns size of given ptr */

malloc的全称是memory allocation,中文叫动态内存分配,用于申请一块连续的指定大小的内存块区域以void*类型返回分配的内存区域地址。extern size_t malloc_size(const void *ptr) 函数的意思就是返回一个指针的占用内存的大小,既然这样我们就把创建的object传进去,看看大小到底是多少,

NSLog(@"obejct: %zd",malloc_size((__bridge const void *)(obejct)));

结果:obejct malloc_size: 16

打印结果的确是16个字节。
根据打印结果结合源码解析我们就可以得知创建NSObject对象的时候系统会分配给其16个字节,但是这个对象只使用了8个字节,另外8个字节是空着的。

而且咱们也可以根据对象地址查看内存空间地址,首先在创建NSObject的时候添加个断点,打印一下obejct对象的地址:0x10062f930,然后找Debug->Debug Workflow -> View Memory,接着将地址输入,就可以查看。

查看内存空间地址步骤

NSObject内存空间地址

根据内存空间地址,我们也可以发现。isa占用了前面8个字节(因为查看的是16进制,一位16进制代表4位二进制,所以两位16进制就是8位二进制也就是一个字节),而后面八个字节都是00,再后面就是其他内存了。

简单画张图表也就是页面这个样子


NSObject内存分析

虽然弄明白了NSObject,但是开发最多的还是用自定义的类。下面就看一下自定义的类所占的内存空间大小。

在此为了方便查看,我就直接在main文件直接创建一个SSPerson类,类里面有两个成员变量

@interface SSPerson : NSObject{
    @public
    int _age;
    int _height;
}
@end

@implementation SSPerson
@end


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
    }
    return 0;
}

然后我们再次main.m文件转为C/C++文件,看看自定义的SSPerson类转成C/C++是什么格式的,方法和前面一样。
然后搜索SSPerson就会发现,

struct SSPerson_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    int _age;
    int _height;
};

我们发现自定的SSPerson类也是转成了一个结构体,内部包含三个成员变量,下面两个是咱们自己写的,NSObject_IVARS 是一个struct NSObject_IMPL结构体类型,前面我们说过,NSObject底层实现就是struct NSObject_IMPL,而其内部就只有一个class类型的 isa 成员,所以这里可以直接当做是Class isa,

struct SSPerson_IMPL {
    Class isa;
    int _age;
    int _height;
};

那么根据之前分析(后面如果没有特别说明都是结合64位环境下计算),isa占8个字节,而且int类型占用4个字节,这样算来,就是
一个isa 8个字节 + int类型 4个字节 + int类型 4个字节 = 16个字节。
我们也可以进一步查看SSPerson对象的内存空间地

image.png

第一个是isa占用8个字节,接着是_age占用4个字节,然后是_height占用4个字节。

那么SSPerson是不是占16个字节,我们打印一下便知。

NSLog(@"SSPerson InstanceSize:%zd",class_getInstanceSize([SSPerson class]));
NSLog(@"person malloc_size: %zd",malloc_size((__bridge const void *)(person)));

结果为:
SSPerson InstanceSize:16
person malloc_size: 16

由此可我们也可以得知,创建SSPerson对象的时候系统会分配给其16个字节,而且其使用了16个字节。

如果感兴趣,可以进一步尝试,多尝试就会有多发现。

答案:

*系统分配了16个字节给NSObject对象(通过malloc_size函数获得)
*但NSObject对象内部只使用了8个字节的空间(64bit环境下,可以通过class_getInstanceSize函数获得)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容