[CH1-Q1] OC 对象的本质——一个NSObject实例对象占用多少内存?

*Q1:一个NSObject实例对象占用多少内存?*

NSObject 对于每一个iOS开发者来说都很熟悉,因为我们几乎每时每刻都跟其打交道,但是我们可能不知道究竟这个熟悉的实例对象究竟占用我们内存的空间是多少呢?那么下面我们就一起来探讨一下。

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

上面这段代码,相信大家很熟悉,就是alloc分配内存给对象,调用init方法为父类属性进行初始化,然后用指针obj指向我们刚才分配的地址。一个NSObject实例对象占用多少内存,其实就是在问这个obj指针指向的内存空间占用的内存空间是有多大?

要想解决这个问题:
①我们需要理解我们平常编写的OC代码其实就是基于C、C++为基础而"面向对象"的一门语言。
②其次需要知道的是NSObject在内存中究竟是如何布局的?

下面我们先来看看第一个问题解释:



我们日常编写的OC代码最终会在编译器的作用下转成C、C++语言、再到汇编语言、机器语言。通过下面的两张图可以发现OC代码编写的Student类和用C、C++代码写的strut结构体,有异曲同工之妙,那么我们是不是可以认为OC中的类底层代码其实就是C、C++的struct(结构体)?



确实是如此,那我们有没有方法去证明呢?答案是有的,我们可以通过新建一个Mac OS的命令行工程,在main函数中输入:

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

然后打开我们的终端程序,输入

    clang -rewrite-objc main.m -o main.cpp  或者
    xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp 

我们通过使用xcode自带的clang工具把OC代码转成C、C++代码,那上面的两个命令有什么不一样的?我们都知道OC这门语言不单单只能开发iOS上面的应用程序,而且能够开发MacOS、WatchOS等平台上的一些程序,而第一条命令会把OC代码转换成支持所有平台的C++程序,而第二条命令只会转换成arm64架构的c++程序。之所以编译成arm64架构支持的,是因为如今市面上的iPhone设备都使用该架构。

第一条命令和第二条命令生成的C++代码我们把其拉到最底部,能够发现我们main函数的代码,然后通过搜索NSObject_IMPL找到 stuct NSObject_IMPL结构体。如下图:

然后我们直接回到刚才编写的OC命令行程序,按住Command键点入NSObject类,我们能够看到我们NSObject其实就是下面这段代码:

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

再做一个简单的去除无用的代码即可得到:

    @interface NSObject <NSObject> {
        Class isa;
    }

通过对比struct NSObject_IMPLNSObject内部OC代码,我们可以证明OC中的类底层代码其实就是C、C++的struct(结构体)。那回到我们一开始的问题:一个NSObject实例对象占用多少内存?我们可以发现struct NSObject_IMPL 中的isa成员变量其实是typedef struct objc_class *Class;这个种类型的指针。而我们知道一个类的地址是由他第一个成员变量的地址所决定的,也就是说如果isa在内存中的地址为 0x100400110那么这个NSObject实例对象的地址值就是0x100400110,因此我们能够得出结论指向一个NSObject实例对象的指针obj的地址就是0x100400110

我们又知道一个指针在64位系统中占用的内存空间就是8个字节,那么我们可能会觉得只有一个isa指针的NSObject实例对象,它所占用的内存空间就是8个字节,其实这是不对的,其实一个NSObject实例对象占用的内存空间为16个字节,为什么呢?我们不妨通过两个函数来验证一下。通过使用<objc/runtime.h>中的class_getInstanceSize方法和<malloc/malloc.h>中的malloc_size方法来打印一下NSObject实例对象所占用的内存空间。

NSLog(@"class_getInstanceSize: %zd",class_getInstanceSize([NSObject class]));
NSLog(@"malloc_size: %zd",malloc_size((__bridge const void *)(obj)));

打印出来的分别是8和16,那为什么我们刚才说的是16个字节而不是8个字节呢?而且这两个方法有什么区别呢?为什么是以malloc_size为标准呢?其实<objc/runtime.h>中的class_getInstanceSize方法所得到的是objc对象实际需要的内存大小,而<malloc/malloc.h>中的malloc_size方法所得到的是objc对象实际分配的内存大小。那为什么一个NSObject对象明明只需要8个字节的内存大小就可以了,但是还是分配到了16个字节大小的内存空间?对于这个问题我们可以通过阅读objc4源码来得到答案,地址https://opensource.apple.com/tarballs/ 。下载最新版本的objc4源码。

通过跟踪obj4中alloc和allocWithZone两个函数的实现,会发现这个连个函数都会调用一个instanceSize的函数:

    size_t instanceSize(size_t extraBytes) {
        size_t size = alignedInstanceSize() + extraBytes;
        // CF requires all objects be at least 16bytes.
        if (size < 16) size = 16;
        return size;
    }

这个函数的代码很简单,返回的结果就是系统给一个对象分配内存的大小。当对象的实际大小小于16时,系统就返回16个字节的大小。也就是说16个字节大小是系统的最低消费。还是用坐车的例子来说明一下,假如有8个人想坐车,他们打电话叫车说要一辆能坐8个人大小的车,对方说sorry我们没有坐8个人大小的车,我们这里最小的就是坐16个人的车。最后来了一辆坐16个人的车,拉了8个人开走了。车就好比一个NSOject对象,车上的乘客就好比是对象中的成员,车的大小或者说载客数量就相当于一个对象占用的内存大小,车上实际的乘客数量就是对象中成员的大小。所以说一个NSObject对象占用多少内存,我想应该很明白了。

总结:
    系统分配了16个字节给NSObject 对象(通过malloc_size获得)
    但NSObject对象内部只使用了8个字节(64bit 通过class_getInstanceSize)
另外:
    我们可以通过view memory、lldb的指令去验证我们的结论,这些操作会再后面制作的视频讲解中附上。
下一节:
    会继续深入,推算针对我们自定义的类内存布局和对象占用的内存空间。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念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

推荐阅读更多精彩内容