上一篇文章我們講到 alloc 在開辟內存空間之前,對對要分配的內存空間提前進行計算,并最終使用 16 字節對其方法進行對其,提升了讀取的效率。但是16 字節對其之前,如何計算對象實際需要的空間呢?
1. 對象內存分析
先展示一段測試代碼
@interface LGPerson : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *name1;
@property (nonatomic, assign) short a;
@property (nonatomic, assign) int b;
@property (nonatomic, assign) double c;
@end
// 調用
LGPerson *person = [[LGPerson alloc] init];
NSLog(@"sizeof——%lu", sizeof([person class]));
NSLog(@"class_getInstanceSize——%lu", class_getInstanceSize([person class]));
NSLog(@"malloc_size——%lu", malloc_size((__bridge const void *)(person)));
sizeof——8
class_getInstanceSize——40
malloc_size——48
這里可以發現,對于類本身,sizeof 可以查詢到它所占用的地址空間只有 8 位。
而 class_getInstanceSize 為何是 40?沒有 16 進制對其?
其實 class_getInstanceSize 獲取到的是實例對象實際需要占用的內存空間,而且實際使用一般只會做 8 字節對齊。
而 malloc_size 則是系統實際為該實例對象分配的空間。也就是經過 16 字節對其后的效果。
2. 結構體內存對齊
看完了對象,我們看下結構體是如何進行內存對齊的。(實際上要比對象對其要簡單些,并且不涉及 16 進制對其的優化,能更直觀的得出內存空間實際占用的計算)
預備知識
在展示實例之前,我們先看下實例中用到的類型的內存空間情況
NSLog(@"double size %lu", sizeof(double));
NSLog(@"int size%lu", sizeof(int));
NSLog(@"short size %lu", sizeof(short));
NSLog(@"char size %lu", sizeof(char));
NSLog(@"NSString size %lu", sizeof(NSString *));
// double size 8
// int size 4
// short size 2
// char size 1
// NSString * size 8(存儲地址)
初級案例
ok,這些類型的空間大小大家應該熟悉了。下面介紹實例
typedef struct Str1 {
int o;
short s;
char a;
double c;
NSString *x;
} S1;
typedef struct Str2 {
double a;
int d;
short b;
char c;
S1 s1;
} S2;
NSLog(@"%lu", sizeof(S1)); // 24
NSLog(@"%lu", sizeof(S2)); // 40
我們先來看下結構體 Str1 的內存大小 - 16。
實際上,基礎類型的存儲位置為棧內存,而結構體的存儲也使用了內存對其的技術,用于減少 cpu? 的訪問內存次數,提升讀取效率。
首先我們需要了解一下結構體內存對其的規則:
- 結構體變量的首地址是其最長基本類型成員的整數倍;
- 結構體每個成員相對于結構體首地址的偏移量(offset)都是成員大小的整數倍,如不滿足,對前一個成員填充字節以滿足;
- 結構體的總大小為結構體最大基本類型成員變量大小的整數倍;
- 結構體中的成員變量都是分配在連續的內存空間中。
typedef struct Str1 {
int o; // 占用 4 字節,分別為 0 ~ 3
short s; // 占用 2 字節,分別為 4 ~ 5
char a; // 占用 1 字節, 分別為 6
NSString *x; // 占用8字節, 但由于規則2限制,需要對空間 7 進行填充,實際存儲位置為 8 ~ 15
double c; // 占用8字節,分別為 16 - 23
} S1; // 根據規則3,最大成員大小為8,0~23 總共占用24字節,剛好符合 8 的倍數。
NSLog(@"%lu", S1); // 24
所以,根據規則,我們得出上述結構體 Str1 內存大小為 24 字節。
進階案例
上面案例中,str2 是一個嵌套類型結構體,他的大小又是如何求得的呢?
??知識點:結構體嵌套的內存對其方式
如果一個結構體B里嵌套另一個結構體A,還是以最大成員類型的字節對齊,但是結構體A存儲起點為A內部最大成員整數倍的地方。(struct B里存有struct A,A里有char,int,double等成員,那A應該從8的整數倍開始存儲。),結構體A中的成員的對齊規則仍滿足自身的規則
typedef struct Str2 {
double a; // 8字節 0 ~ 7
int d; // 4 字節: 8 ~ 11
short b; // 2字節: 12 ~ 13
char c; // 1字節: 14
S1 s1; // 根據上面的結果,此處大小為 24 字節。此處需要注意,依據結構體嵌套對其方式,存儲起始點則為最長 8 字節的倍數,即 16 ~ 39
} S2; // 綜上,40 剛好為 8 的整數倍。即如 sizeof 得出的結果為,40