iOS一個(gè)對(duì)象占用多少空間?

GitHub 地址

1.獲取 NSObject 對(duì)象占用的空間

  1. 創(chuàng)建一個(gè) NSObject 對(duì)象, 分別調(diào)用 class_getInstanceSize 和 malloc_size 方法
NSObject *obj = [[NSObject alloc] init];

// 需要導(dǎo)入 #import <objc/runtime.h>
NSLog(@"%zd", class_getInstanceSize([NSObject class]));

// 需要導(dǎo)入 #import <malloc/malloc.h>
NSLog(@"%zd", malloc_size((__bridge const void *)(obj)));
  1. 得到輸出:
2019-11-05 15:10:22.210505+0800 Interview001-OC對(duì)象的本質(zhì)[9215:816956] 8
2019-11-05 15:10:22.210675+0800 Interview001-OC對(duì)象的本質(zhì)[9215:816956] 16
  1. 可以看出系統(tǒng)開辟了16個(gè)字節(jié), 但是該對(duì)象只是用了8個(gè)字節(jié)
    這是為什么了?

  2. 點(diǎn)進(jìn) NSObject 可以看到下面的代碼, NSObject 對(duì)象真正的是一個(gè)結(jié)構(gòu)體指針, 所以會(huì)占用8個(gè)字節(jié)

/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;

@interface NSObject <NSObject> {
    Class isa ;
}
  1. https://opensource.apple.com/tarballs/ 找到 runtime 源碼, 搜索 class_getInstanceSize 可以找到下面的代碼, 可以看出 class_getInstanceSize 獲取的是類的成員變量的 size, 并不是一個(gè)對(duì)象所占用的空間
size_t class_getInstanceSize(Class cls)
{
    if (!cls) return 0;
    return cls->alignedInstanceSize();
}

// Class's ivar size rounded up to a pointer-size boundary.
uint32_t alignedInstanceSize() {
    return word_align(unalignedInstanceSize());
}

size_t instanceSize(size_t extraBytes) {
    size_t size = alignedInstanceSize() + extraBytes;
    // CF requires all objects be at least 16 bytes.
    if (size < 16) size = 16;
    return size;
}
  1. 那為什么會(huì)分配16個(gè)字節(jié)了? 也是在 runtime 里找到了答案, 當(dāng) size < 16 時(shí), 會(huì)默認(rèn)分配16個(gè)字節(jié)

可以參照 這篇文章 把.m文件編譯成.cpp文件, 查看 NSObject真正的實(shí)現(xiàn), 可以理解是對(duì)應(yīng) c++ 里的結(jié)構(gòu)體

我的 GitHub 里有已經(jīng)編譯后的.cpp文件 大家可以查看

2. 自定義對(duì)象占用的空間

自定義一個(gè) Person類

@interface Person : NSObject
{
}
@end

@implementation  Person
@end
  1. 打印當(dāng)前對(duì)象
Person *p = [[Person alloc] init];

NSLog(@"%zd", class_getInstanceSize([Person class]));
NSLog(@"%zd", malloc_size((__bridge const void *)(p)));

得到的結(jié)果和 NSObject 對(duì)象一樣的

2019-11-05 15:30:11.991698+0800 Interview001-OC對(duì)象的本質(zhì)[9360:864510] 8
2019-11-05 15:30:11.991704+0800 Interview001-OC對(duì)象的本質(zhì)[9360:864510] 16
  1. 給自定義對(duì)象添加1個(gè)成員變量, 再次打印
@interface Person : NSObject
{
    int _age;
}
@end
2019-11-05 15:31:56.767972+0800 Interview001-OC對(duì)象的本質(zhì)[9385:868476] 16
2019-11-05 15:31:56.767978+0800 Interview001-OC對(duì)象的本質(zhì)[9385:868476] 16
  1. 再次添加成員變量, 再次打印
@interface Person : NSObject
{
    int _age;
    int _number;
}
@end
2019-11-05 15:34:16.503491+0800 Interview001-OC對(duì)象的本質(zhì)[9414:874058] 16
2019-11-05 15:34:16.503497+0800 Interview001-OC對(duì)象的本質(zhì)[9414:874058] 16
  1. 很奇怪, 沒有變化啊? 我們編程成.cpp再來分析
struct Person_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    int _age;
    int _number;
};
  1. 這時(shí)自定義對(duì)象包含了

    • 1個(gè)結(jié)構(gòu)體指針 (8字節(jié))
    • 1個(gè) int _age (4字節(jié))
    • 1個(gè) int _number (4字節(jié))

    8 + 4 + 4 = 16 字節(jié)

  • 沒有添加任何成員變量時(shí), 只有1個(gè)結(jié)構(gòu)體指針, 所以會(huì)是8字節(jié)
  • 添加1個(gè)成員變量時(shí), 有1個(gè)結(jié)構(gòu)體指針, 和1個(gè) int 類型的值, 那就是 8 + 4 = 12, 為什么 class_getInstanceSize 得到的結(jié)果是16了. 其實(shí)擴(kuò)容基數(shù)是8(內(nèi)存對(duì)齊原因, 結(jié)構(gòu)體的大小必須是最大成員大小的倍數(shù)), 8個(gè)字節(jié)不夠了, 就開辟了8*2個(gè)字節(jié), 依此類推
  • 添加了2個(gè)成員變量時(shí), 有1個(gè)結(jié)構(gòu)體指針, 和2個(gè) int 類型的值,那就是 8 + 4 + 4 = 16
  1. 再添加一個(gè)成員變量
@interface Person : NSObject
{
    int _age;
    int _number;
    int _sex;
}
@end
2019-11-05 15:48:24.959113+0800 Interview001-OC對(duì)象的本質(zhì)[9511:896453] 24
2019-11-05 15:48:24.959120+0800 Interview001-OC對(duì)象的本質(zhì)[9511:896453] 32

可以看出 class_getInstanceSize 已經(jīng)是8*3=24字節(jié)了
但是 malloc_size 卻是 32了, 因?yàn)槭且?6為基數(shù)來擴(kuò)容的, 因?yàn)?iOS 在分配對(duì)象內(nèi)存時(shí), 都是以16倍數(shù)來分配的

3.結(jié)論

  • 1個(gè) NSObject 對(duì)象占用了16個(gè)字節(jié)
  • 1個(gè)自定義對(duì)象占用了幾個(gè)字節(jié), 需要有多少成員變量, 同時(shí)還要計(jì)算上 NSObject 的 isa 指針大小, 同時(shí)為了對(duì)齊, 必須是16的倍數(shù)

4.注意點(diǎn)

- 如果自定義類有繼承關(guān)系, 如 Student 繼承于 Person, 要繼續(xù)父類的成員變量
- 注意內(nèi)存對(duì)齊問題, iOS 分配內(nèi)存問題

參考:
http://www.itdecent.cn/p/c22279cba38d
https://juejin.im/post/5abdd56df265da2396127e6b
https://www.iteye.com/blog/jakend-1839987
https://github.com/ludx/The-Lost-Art-of-C-Structure-Packing

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容