OC 對象原理探索(二)

目錄

  • 1. 對象的內存影響因素
  • 2. 成員變量在內存中的存儲情況
  • 3. 結構體內存對齊
    ?3.1 類型占用字節數表格
    ?3.2 內存對齊原則
    ???1、數據成員對齊規則
    ???2、結構體作為成員
    ???3、結構體總大小
    ?3.3 舉例驗證
  • 4. sizeofclass_getInstanceSizemalloc_size
  • 5. malloc源碼分析
    ?5.1 源碼下載
    ?5.2 源碼分析
  • 6. 總結

1. 對象的內存影響因素

先創建一個SSLPerson類:

@interface SSLPerson : NSObject {
    NSString *_hobby;
}

@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *nickName;

- (void)eat;

@end

打印一下它的內存大小:

nickName屬性注釋掉:

//@property (nonatomic, copy) NSString *nickName;

查看打印結果:

eat方法注視掉:

//- (void)eat;

查看打印結果:

把成員變量_hobby注視掉:

//    NSString *_hobby;

查看打印結果:

通過上面的打印,我們發現屬性成員變量影響了對象內存,方法沒有影響,所以我們可以得出最終結論:只有成員變量會影響對象內存。

2. 成員變量在內存中的存儲情況

我們修改一下SSLPerson類:

@interface SSLPerson : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *nickName;
@property (nonatomic, copy) NSString *hobby;
@property (nonatomic, assign) int age;
@property (nonatomic) double height;

@property (nonatomic) char a;
@property (nonatomic) char b;

@end

創建SSLPerson類的實例p,為屬性賦值,用x/8gx命令打印出這些值在內存中的存儲情況:

找出ssl王老五180.5

找出24ab

注:aASCII碼97,bASCII碼98

通過打印結果我們發現24ab被存儲到了同一個8字節內,這里就涉及到了內存對齊的一些原則,下面繼續探究。

3. 結構體內存對齊

3.1 類型占用字節數表格

C OC 32位 64位
bool BOOL(64位) 1 1
signed char (__signed char)int8_t、BOOL(32)位 1 1
unsigned char Boolean 1 1
short int16_t 2 2
unsigned short unichar 2 2
int int32_t NSInteger(32位)、boolean_t(32位) 4 4
unsigned int boolean_t(64位)、NSUInteger(32位) 4 4
long NSInteger(64位) 4 8
unsigned long NSUInteger(64位) 4 8
long long int64_t 8 8
float CGFloat(32位) 4 4
double CGFloat(64位) 8 8

3.2 內存對齊原則

1、數據成員對齊規則

結構體struct或聯合體union的數據成員,第一個數據成員放在offset0的位置,以后每個數據成員存儲的起始位置要從該成員大小或者成員的子成員大小(只要該成員有子成員,比如說數組,結構體等)的整數倍數開始(比如int4個字節,則要從4的整數倍地址開始存儲)。

2、結構體作為成員

如果一個結構里有某些結構體成員,則結構體成員要從其內部最大元素大小的整數倍地址開始存儲,(struct a中存有struct bb中有charintdouble等元素,那么b應該從8的整數倍開始存儲),因為double為最大子元素,占用8個字節。

3、結構體總大小

結構體總大小,也就是sizeof的結果,必須是其內部最大成員整數倍,不足的要補齊。

3.3 舉例驗證

struct Struct1 {
    double a;       // 占8字節 存放在[0 7]
    char b;         // 占1字節 下一個索引8是1的整數倍,存放在[8]
    int c;          // 占4字節 下一個索引9不是4的整數倍,所以空出9,10,11,存放在 [12 13 14 15]
    short d;        // 占2字節 下一個索引16是2的整數倍,存放在[16 17]
}struct1;           // 總區間為[0...17],大小為18,取最大元素double8字節的整倍數,所以總大小為24

struct Struct2 {
    double a;       // 占8字節 存放在[0 7]
    int b;          // 占4字節 下一個索引8是4的整數倍,存放在[8 9 10 11]
    char c;         // 占1字節 下一個索引12是1的整倍數,存放在[12]
    short d;        // 占2字節 下一個索引13不是2的整倍數,所以空出13 存放在[14 15]
}struct2;           // 總區間為[0...15],大小為16,取最大元素double8字節的整倍數,所以總大小為16

struct Struct3 {
    double a;               // 占8字節 存放在[0 7]
    int b;                  // 占4字節 下一個索引8是4的整數倍,存放在[8 9 10 11]
    char c;                 // 占1字節 下一個索引12是1的整數倍,存放在[12]
    short d;                // 占2字節 下一個索引13不是2的整數倍,所以空出13,存放在[14 15]
    int e;                  // 占4字節 下一個索引16是4的整數倍,存放在[16 17 18 19]
    struct Struct1 str1;    // 占24字節 下一個索引20不是str1中double8字節的整數倍,所以空出20 21 22 23,最后存放在[24.....47]
    struct Struct2 str2;    // 占16字節 下一個索引48是str2中double8字節整數倍,存放在[48.....64]
    short f;                // 占2字節 下一個索引65不是2的整數倍,所以空出65,存放在[66,67]
}struct3;                   // 總區間為[0...67],大小為68,取最大元素double 8字節的整倍數,所以總大小為72

NSLog(@"Struct1:%lu -- Struct2:%lu -- Struct3:%lu",
            sizeof(struct1), sizeof(struct2), sizeof(struct3));

查看打印結果:

Struct1:24 -- Struct2:16 -- Struct3:72

4. sizeofclass_getInstanceSizemalloc_size

修改SSLPerson類:

@interface SSLPerson : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *nickName;
@property (nonatomic, assign) int age;
@property (nonatomic, assign) long height;

@end

main.m中代碼:

#import "SSLPerson.h"
#import <objc/runtime.h>
#import <malloc/malloc.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        SSLPerson *person = [SSLPerson alloc];
        person.name      = @"lee";
        person.nickName  = @"ssl";
        
        NSLog(@"person %@",person);
        NSLog(@"sizeof %lu",sizeof(person));
        NSLog(@"person %lu",class_getInstanceSize([SSLPerson class]));
        NSLog(@"person %lu",malloc_size((__bridge const void *)(person)));
    }
    return 0;
}

查看打印結果:

解釋:

  • sizeof這里計算的是person指針的大小,指針統一為8字節;
  • class_getInstanceSize計算的是isa指針加成員變量占用的內存:name(NSString``8字節) + nickName(NSString``8字節) + age(int``4字節) + height(long``8字節) + isa(來自NSObject``8字節) = 36字節,按照8字節對齊,最終為40字節;
  • malloc_size計算的是實際向系統申請開辟的內存空間:40字節向系統申請時,遵循16字節對齊原則,最終為48字節。

malloc是如何申請內存的呢,我們接下來通過源碼來進行分析。

5. malloc源碼分析

5.1 源碼下載

工程中點擊malloc_size定位源碼所在位置:

malloc 源碼下載地址,我們以317.40.8版本為例進行分析。

5.2 源碼分析

我們在main.m中添加代碼,用40字節去申請內存:

#import <Foundation/Foundation.h>
#import <malloc/malloc.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        // 用 40 字節去申請內存
        void *p = calloc(1, 40);
        NSLog(@"開辟字節數:%lu",malloc_size(p));
    }
    return 0;
}

查看打印結果:

確實還是開辟了48字節的空間,下面通過斷點調試查看源碼。

斷點進入calloc

斷點進入:_malloc_zone_calloc

通過返回值找到核心代碼ptr = zone->calloc(zone, num_items, size),點擊進入calloc(zone, num_items, size)發現找不到方法實現。

可以通過po的方式找到應該調用的方法default_zone_calloc

  • 為什么可以打印獲取:因為有賦值,就會有存儲值,就可以打印輸出。
  • 第二種方式:除了輸出的方式,還可以通過匯編找方法的方式找到方法的真實調用。

斷點進入default_zone_calloc

還是找不到方法,通過p zone->calloc獲取到方法nano_calloc

注:ppo打印的更詳細。

斷點進入nano_calloc

根據返回值定位核心代碼:_nano_malloc_check_clear,斷點進入:

根據返回值找到segregated_size_to_fit關鍵函數,斷點進入:

這里是16字節對齊算法,40經過對齊后得到48,這就是最終會開辟48字節的原因。

6. 總結

  • 堆區中,對象的內存以16字節對齊;
  • 成員變量,以8字節對齊。
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,494評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,283評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,953評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,714評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,410評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,940評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,776評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,210評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,654評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容