iOS 一個OC對象在內存中的布局&&占用多少內存

一.先來看看我們平時接觸的NSObject

  • NSObject *objc = [[NSObject alloc]init]的本質
    在內存中,這行代碼就把objc轉在底層實現中轉成了一個結構體,其底層C++編譯成結構體為:
struct NSObject_IMPL {
    Class isa;
};

在64位機中,一個isa占8個字節,在32位機中,一個isa占4個字節(當然蘋果后面的機型都是64位的,這里我們著重講解64位機)

  • 我們先來看看這個創建好的objc占多少個字節
int main(int argc, char * argv[]) {
    
    @autoreleasepool {
        // Setup code that might create autoreleased objects goes here.
        //定義一個objc
        NSObject *objc = [[NSObject alloc]init];
        //打印內存
        NSLog(@"tu-%zd",class_getInstanceSize([NSObject class]));
        NSLog(@"tu-%zd",malloc_size((__bridge const void *)(objc)));
    }
    
}

其打印結果為
objc打印結果
  • 為什么一個是8一個是16
    • 我們先來認識一下class_getInstanceSize、malloc_size的區別
      1.class_getInstanceSize:是一個函數(調用時需要開辟額外的內存空間),程序運行時才獲取,計算的是類的大小(至少需要的大小)即實例對象的大小->結構體內存對齊
      2.創建的對象【至少】需要的內存大小不考慮malloc函數的話,內存對齊一般是以【8】對齊
      3.#import <objc/runtime.h>使用這個函數時倒入runtime運行時

    • malloc_size:堆空間【實際】分配給對象的內存大小 -系統內存對齊

      1. 在Mac、iOS中的malloc函數分配的內存大小總是【16】的倍數 即指針指向的內存大小
      2. import <malloc/malloc.h>使用時倒入這個框架
  • sizeof:是一個運算符,獲取的是類型的大小(int、size_t、結構體、指針變量等),這些數值在程序編譯時就轉成常數,程序運行時是直接獲取的

看到上面對兩個函數的認識,應該知道為什么輸出的一個是8,一個是16了吧,當內存申請<16時,在底層分配的時候,系統會默認最低16個字節,系統給objc16個字節,而objc用到的是8個字節(沒添加任何成員變量之前)

二.內存對齊

  • 在上面的基礎上我們新建一個類Student繼承NSObject,那么對于student的底層C++編譯實現就變成了:
struct Student {
    struct NSObject_IMPL NSOBJECT_IVARS;
};

也就是說,繼承關系,子類直接將父類的isa引用進來

  • 對于class_getInstanceSize(也就是類本質的內存對其)
    1.在student中創建成員變量:
@interface Student : NSObject
{
    @public
    int _age;
    int _no;
    int _tc;
}
@end

其底層C++編譯結構體就變成了

struct Student {
    struct NSObject_IMPL NSOBJECT_IVARS;
    int _age;
    int _no;
    int _tc;
};

  • 打印結果:
 //定義一個objc
        Student *objc = [[Student alloc]init];
        //打印內存
        NSLog(@"tu-%zd",class_getInstanceSize([Student class]));
        NSLog(@"tu-%zd",malloc_size((__bridge const void *)(objc)));

2020-09-08 12:35:27.158568+0800 OC底層[1549:79836] tu-24
2020-09-08 12:35:27.159046+0800 OC底層[1549:79836] tu-32

  • 先來說說24的由來

由于創建對象的時候,內存是以8對齊,上面我們講到一個對象里面包含了一個isa占8個字節,對于student來說它有四個成員變量,isa,age,no,tc,共占8+4+4+4=20字節,但是由于內存以8對齊的原因,我們看到的輸出是24,
結構體8位對齊

所以class_getInstanceSize在計算實例大小的時候就是24,其白色區域表示空出了四個字節

  • 再來看看32的由來
    上面我們說到malloc_size指的是實際堆分配的空間,它以16字節對齊


    堆內存對齊

可以看到,空白的區域為空出了12個字節,總共為32個字節

三.添加屬性

  • 添加屬性
@interface Student : NSObject
{
    @public
    int _age;
    int _no;
    int _tc;

}
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSArray *array;
@end

其在底層C++編譯就變成了

struct Student {
    struct NSObject_IMPL NSOBJECT_IVARS;
    int _age;
    int _no;
    int _tc;
    NSString _name;
    NSArray _array;
};

默認的會將屬性生成的_name添加進結構體中,計算相應的大小

總結:所以在實際計算類的占用空間大小的時候,根據添加的成員變量就可以計算出一個實例占用的內存大小(即計算出結構體的大小24,然后告訴系統,系統調用calloc分配內存的時候按照16對齊原則分配)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。