從結(jié)構(gòu)體內(nèi)存對齊到OC對象內(nèi)存對齊

1捐川、結(jié)構(gòu)體內(nèi)存對齊

結(jié)構(gòu)體對齊規(guī)則:

1:數(shù)據(jù)成員對?規(guī)則:結(jié)構(gòu)(struct)(或聯(lián)合(union))的數(shù)據(jù)成員和橙,第?個數(shù)據(jù)成員放在offset為0的地?,以后每個數(shù)據(jù)成員存儲的起始位置要從該成員??或者成員的?成員??(只要該成員有?成員,?如說是數(shù)組寺枉,結(jié)構(gòu)體等)的整數(shù)倍開始(?如int為4字節(jié),則要從4的整數(shù)倍地址開始存儲揭芍。 min(當(dāng)前開始的位置m n) m = 9 n = 4 9 10 11 12

2:結(jié)構(gòu)體作為成員:如果?個結(jié)構(gòu)?有某些結(jié)構(gòu)體成員,則結(jié)構(gòu)體成員要從其內(nèi)部最?元素??的整數(shù)倍地址開始存儲.(struct a?存有struct b,b?有char,int ,double等元素,那b應(yīng)該從8的整數(shù)倍開始存儲.)

3:收尾?作:結(jié)構(gòu)體的總??,也就是sizeof的結(jié)果,.必須是其內(nèi)部最?成員的整數(shù)倍.不?的要補(bǔ)?胳搞。

根據(jù)上面規(guī)則,我們來進(jìn)行幾道練習(xí)題:

struct LGStruct1 {
    int a;      // [0-3]
    char b;        // [4]
    int c;         // 5 6 7 [8 9 10 11]
    short d;       // [12 13]
}struct1;
//內(nèi)部最大是int称杨,也就是4位肌毅,所以需要4字節(jié)對齊, struct1就是16字節(jié)

//同理:struct2的內(nèi)存對齊計算
struct LGStruct2 {
    int a;           // [0-3]
    int b;         // [4 - 7]
    char c;        // [8]
    short d;       // 9  [10 11]
}struct2;
//內(nèi)部最大是int姑原,也就是4位悬而,所以需要4字節(jié)對齊, struct1就是12字節(jié)

//同理:struct3的內(nèi)存對齊計算
struct LGStruct3 {
    
    double a;    // [0-7]
    int e;       // [8 9 10 11]
    struct LGStruct1 str;  //最大元素是int锭汛,所以相當(dāng)于4位倍數(shù)開始存儲笨奠,[12 - 28]
}struct3;
//內(nèi)部最大元素是double袭蝗,所以需要8字節(jié)對齊, struct3 就是 32字節(jié)

struct LGStruct4 {
    char a;          // [0]
    struct LGStruct3 str;  //最大元素是double般婆,8的倍數(shù)開始存儲到腥,[8 - 39]
}struct4;
//內(nèi)部最大元素是LGStruct3,其內(nèi)部最大是8位蔚袍, struct4 就是 40字節(jié)

做完上面練習(xí)題乡范,對結(jié)構(gòu)體的內(nèi)存規(guī)則應(yīng)該是比較熟悉了,這里發(fā)現(xiàn)了一個奇怪的現(xiàn)象页响,也就是struct1和struct2存儲的內(nèi)容是一樣多的篓足,但是大小卻不一樣。之前探索了Objective-C中的class和對象在C++中的原理實現(xiàn)知道對象存儲是一個結(jié)構(gòu)體闰蚕,存儲方式如:

struct Persion_2415_IMPL {                 //存儲Persion的數(shù)據(jù)是一個struct Persion_2415_IMPL的數(shù)據(jù)結(jié)構(gòu)
    struct NSObject_IMPL NSObject_IVARS;
    int _age_2415;
    NSString *_name_2415;
};

從上面存儲的結(jié)構(gòu)可以猜想栈拖,存儲內(nèi)存大小很可能和成員方法、類方法沒有關(guān)系没陡。測試之后發(fā)現(xiàn)涩哟,果然是沒有關(guān)系的。

那么考慮我們oc的對象使用的內(nèi)存盼玄,是否和變量的順序有關(guān)系呢贴彼,進(jìn)行如下測試

#import <Foundation/Foundation.h>
#import <objc/runtime.h>

#import <malloc/malloc.h>

@interface Person : NSObject
@end
@implementation Person
{
    //isa
    double a; //[8-15]
    char b;   //[16]
    double c; //[24 - 31]
    char d;   //[32]
    double e; // [40 - 47]
    char f;   //[48]
    double g; // [56 - 63]
    char h;   // [64]
}

@end

@interface Student : NSObject
@end
@implementation Student
{
    //isa
    double a; //[8-15]
    double b; //[16-23]
    double c; //[24-31]
    double d; //[32-40]
    
    char e;   // [41]
    char f;   // [42]
    char g;   // [43]
    char h;   // [44]
}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        Person * per = [[Person alloc] init];
        Student * stu = [[Student alloc] init];

        NSLog(@"class_getInstanceSize -- %ld %ld",class_getInstanceSize([Person class]), class_getInstanceSize([Student class]));
        
        NSLog(@"malloc_size -- %ld %ld",malloc_size((__bridge void*)(per)), malloc_size((__bridge void*)(stu)));
        
    }
    return 0;
}

/*
輸出結(jié)果:
2021-06-17 16:47:19.435118+0800 004-isa分析[8791:160576] class_getInstanceSize -- 72 48
2021-06-17 16:47:19.435456+0800 004-isa分析[8791:160576] malloc_size -- 80 48
*/

根據(jù)上面結(jié)果發(fā)現(xiàn),原來真的會有內(nèi)存使用的差別埃儿,那么我們可以根據(jù)這個方式來優(yōu)化內(nèi)存器仗。再次探索,如果是作為屬性的話童番,蘋果內(nèi)部會不會幫我們做一些優(yōu)化呢精钮,發(fā)現(xiàn)如果把上面的成員變量改成屬性的話

/*
輸出結(jié)果:
2021-06-17 16:29:54.056023+0800 004-isa分析[8613:153735] class_getInstanceSize -- 48 48
2021-06-17 16:29:54.056347+0800 004-isa分析[8613:153735] malloc_size -- 48 48
*/

由此可以知道,蘋果幫我們使用屬性的時候做了內(nèi)存優(yōu)化剃斧,所以我們?nèi)绻暶鞒蓡T變量轨香,一般最好使用屬性的方式,能夠更加節(jié)約內(nèi)存幼东。

2臂容、為什么要進(jìn)行內(nèi)存對齊呢

由于CPU讀取的時候很多是按照雙字節(jié),4字節(jié)讀取的根蟹,如果不進(jìn)行內(nèi)存對齊的話脓杉,例如結(jié)構(gòu)體

struct s1{
        char a;
        int b;
}
/*
    內(nèi)存不對齊的情況下:
    char a 存儲在 0 字節(jié)
    int b 存儲在   1-4 字節(jié)
    需要讀取b的時候,如果是4字節(jié)讀取的简逮,需要讀2次丽已,提取2次,組合一次
    0 - 3买决, 4 - 7沛婴, 然后再把 0-3中的1-3提取,和4-8中的4提取督赤,再組合出 1-4
    
    如果內(nèi)存對齊的情況下:
    char a 存儲在 0 字節(jié)
    int b 存儲在   4-7 字節(jié)
    需要讀取b的時候嘁灯,如果是4字節(jié)讀取的,需要讀1次躲舌,
    4 - 7 直接讀取成功
    
    這是一種以空間換時間的方式丑婿,能夠很好的提高讀取的效率
*/

3、OC中的內(nèi)存再探索

從剛剛的打印中我們有一個疑問没卸,Person的class_getInstanceSize 和 malloc_size的大小并不一樣羹奉,其中

  • class_getInstanceSize:對象至少需要的大小

  • malloc_size: 對象實質(zhì)的大小

class_getInstanceSize的值,我們從結(jié)構(gòu)體對齊中很容易得出原因约计。但是malloc_size為什么會不一樣呢诀拭。我們開始探索OC中alloc時蘋果的源碼流程。之后再增加具體探索流程和代碼閱讀源碼技巧

發(fā)現(xiàn)alloc的時候煤蚌,會在class_getInstanceSize的基礎(chǔ)上進(jìn)行16字節(jié)的對齊耕挨。所以malloc_size大小會不一樣。

總結(jié):

  1. 我們可以在聲明成員變量的時候尉桩,盡量的使用屬性的方式筒占,這時蘋果會幫我們做好內(nèi)存的優(yōu)化。使得內(nèi)存空間更加節(jié)省
  2. 對象的成員變量大兄├纭(包括isa)相加盡量是16的整數(shù)倍翰苫,可以減少一些浪費
  3. 對象的內(nèi)存使用大小和對象方法,類方法都沒有關(guān)系
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末这橙,一起剝皮案震驚了整個濱河市奏窑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌析恋,老刑警劉巖良哲,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異助隧,居然都是意外死亡筑凫,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進(jìn)店門并村,熙熙樓的掌柜王于貴愁眉苦臉地迎上來巍实,“玉大人,你說我怎么就攤上這事哩牍∨锪剩” “怎么了?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵膝昆,是天一觀的道長丸边。 經(jīng)常有香客問我叠必,道長,這世上最難降的妖魔是什么妹窖? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任纬朝,我火速辦了婚禮,結(jié)果婚禮上骄呼,老公的妹妹穿的比我還像新娘共苛。我一直安慰自己,他們只是感情好蜓萄,可當(dāng)我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布隅茎。 她就那樣靜靜地躺著,像睡著了一般嫉沽。 火紅的嫁衣襯著肌膚如雪辟犀。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天耻蛇,我揣著相機(jī)與錄音踪蹬,去河邊找鬼。 笑死臣咖,一個胖子當(dāng)著我的面吹牛跃捣,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播夺蛇,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼疚漆,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了刁赦?” 一聲冷哼從身側(cè)響起娶聘,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎甚脉,沒想到半個月后丸升,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡牺氨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年狡耻,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片猴凹。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡夷狰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出郊霎,到底是詐尸還是另有隱情沼头,我是刑警寧澤,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站进倍,受9級特大地震影響土至,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜背捌,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一毙籽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧毡庆,春花似錦、人聲如沸烙如。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽亚铁。三九已至蝇刀,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間徘溢,已是汗流浹背吞琐。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留然爆,地道東北人站粟。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像曾雕,于是被迫代替她去往敵國和親奴烙。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,055評論 2 355

推薦閱讀更多精彩內(nèi)容