本章主要由結構體內存對齊到蘋果的屬性重排挺物, 以及
16字節(jié)對齊算法
0x00 -- 獲取內存大小的三種方式
LGPerson *person = [LGPerson alloc];
person.name = @"Cooci";
person.nickName = @"KC";
NSLog(@"%@ - %lu - %lu - %lu",person,sizeof(person),class_getInstanceSize([LGPerson class]),malloc_size((__bridge const void *)(person)));
// 輸出 8 - 40 - 48
獲取內存大小的三種方式:
sizeof(expression-or-type)
小提示 ??: sizeof
的三種語法形式:
int a = 10;
size_t a1 = sizeof(a); // 4
size_t a2 = sizeof a; // 4
size_t a3 = sizeof(int); // 4
int *pa = &a;
size_t p1 = sizeof(pa); // 8
size_t p2 = sizeof pa; // 8
size_t p3 = sizeof(int *); // 8
NSObject *obj = [NSObject alloc] ;
size_t o1 = sizeof(obj); // 8
size_t o2 = sizeof obj; // 8
size_t o3 = sizeof(NSObject*);// 8
sizeof()
是操作符直焙,不是函數(shù)撩扒;其作用是返回一個對象或者類型所占的內存字節(jié)數(shù)。基本數(shù)據(jù)類型
int
,char
,double
,float
等這樣簡單內置數(shù)據(jù)類型扩然,它們的大小和系統(tǒng)相關掸犬, 不同系統(tǒng)下取值也不一樣。如果是結構體通殃,
sizeof
涉及到字節(jié)對齊的問題度液,根據(jù)計算機組成原理
教導我們這樣有助于加快計算機的取數(shù)速度,否則多話指令周期画舌。讓寬度為2
的節(jié)本數(shù)據(jù)類型short等
堕担,都位于能被2
整除的低智商;讓寬度為4
的基本數(shù)據(jù)類型int等
骗炉,都位于能被4
整除的地址上照宝,以此類推,這樣句葵,倆個數(shù)中間就可能需要加入填充的字節(jié)
厕鹃,所以整個結構體的sizeof
值就增長了。詳細對齊規(guī)則看文章下方結構體對齊
乍丈。 這里不再贅述剂碴。
class_getInstanceSize
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() const {
return word_align(unalignedInstanceSize());
}
#ifdef __LP64__
# define WORD_SHIFT 3UL
# define WORD_MASK 7UL
# define WORD_BITS 64
#else
# define WORD_SHIFT 2UL
# define WORD_MASK 3UL
# define WORD_BITS 32
#endif
static inline uint32_t word_align(uint32_t x) {
return (x + WORD_MASK) & ~WORD_MASK;
}
這個函數(shù)是runtime
提供的一個API
函數(shù),獲取類的實例對象所占用的內存大小
轻专。
通過源碼可知道 class_getInstanceSize
函數(shù)獲取的的對象大小是8
字節(jié)對齊忆矛。
這個函數(shù)依據(jù)對象內部的屬性
變化而變化;如果沒有屬性, 只繼承了NSObject
催训,則類的實例對象實際占用的內存大小是8
洽议,
malloc_size
malloc_size
是系統(tǒng)實際開辟的內存空間,class_getInstanceSize
是實際占用的空間漫拭。根據(jù)文章頂部
代碼實例1
打印可驗證亚兄。這個是由系統(tǒng)完成的,從上面的分析看得出采驻, 實際占用和實際分配的大小是不一樣的审胚。
0x01 -- 結構體內存對齊
這里可以參考我之前寫的文章底層必備c/c++知識結構體
struct LGStruct1 {
double d; // 0-7
char c; //8
int i;
short s; //9 10 11 12 13 14 15
};
struct LGStruct2 {
double d; //8 0-7
int i; //4 8-11
char c; //1 12
short s; //2 13 14 15
};
int main() {
NSLog(@"%lu", sizeof(struct LGStruct1)); // 24
NSLog(@"%lu", sizeof(struct LGStruct2)); // 16
return 0;
}
上面片段代碼Test1
和Test2
的sizeof
是多少?
從打印可得知LGStruct1
的內存大小為24
; LGStruct2
為16
;
倆個結構體 礼旅,數(shù)據(jù)類型一致膳叨,順序不一樣, 導致輸出的結果也不一樣痘系, 這就涉及到
內存對齊
菲嘴;至于為什么系統(tǒng)要做這件事; 上面說
sizeof
的時候也說了, 簡單來說碎浇,提高性能临谱。
內存對齊規(guī)則
【原則一】 數(shù)據(jù)成員的對齊規(guī)則可以理解為
min(m, n)
的公式, 其中m
表示當前成員的開始位置
,n
表示當前成員所需要的位數(shù)
。如果滿足條件m 整除 n
(即m % n == 0
),n
從m
位置開始存儲, 反之繼續(xù)檢查 m+1 能否整除 n
, 直到可以整除, 從而就確定了當前成員的開始位置奴璃。【原則二】數(shù)據(jù)成員為結構體:當結構體嵌套了結構體時悉默,作為數(shù)據(jù)成員的結構體的
自身長度
作為外部結構體的最大成員的內存大小,比如結構體a嵌套結構體b苟穆,b中有char抄课、int、double等雳旅,則b的自身長度
為8【原則三】最后
結構體的內存大小
必須是結構體中最大成員內存大小
的整數(shù)倍跟磨,不足的需要補齊。
對齊規(guī)則驗證
根據(jù)上面的實例代碼攒盈, 畫了一章對齊示意圖
- 根據(jù)規(guī)則一 進行內部成員對齊抵拘。所占用字節(jié)
18
個; - 根據(jù)規(guī)則三 整體對齊后型豁,占用字節(jié)
24
個僵蛛。
結構體嵌套對齊
struct mystruct4 {
int a;
struct mystruct5 {
short c;
double b;
char c1;
}str5;
int d;
}s4;
printf("mystruct4內存大小 %lu\n", sizeof(s4)); // 40
printf("mystruct5內存大小 %lu\n", sizeof(s4.str5)); // 24
結構體嵌套對齊的算法規(guī)則是
-
int
從0
開始, 占4
個字節(jié)迎变, 位置是0 1 2 3
充尉。 -
str5
按照規(guī)則二從double
算, 空白填充4 5 6 7
;從8-24
是嵌套結構體所占大小衣形。 - 接著
25
,不滿足規(guī)則一驼侠,25 26 27
填充空白,28 29 30 31
占位。 - 一共使用了
32
個字節(jié)倒源,按照規(guī)則三整體對齊苛预,是40
個字節(jié)。
0x02 -- 內存優(yōu)化(屬性重排)
x指令
打印對象內存地址 ==memory read person
x/4gx
以16進制打4個印人可以方便看的內存
@interface Other: NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *nickName;
@property (nonatomic) char sex;
@property (nonatomic, assign) int age;
@property (nonatomic, assign) long height;
@property (nonatomic) char garde;
@end
@implementation
@end
Other *person = [Other alloc];
person.name = @"liming";
person.nickName = @"ll";
person.sex = "m";
person.garde = "A";
person.age = 23;
通過LLDB
命令查看對象
的內存布局相速。x
命令查看的不夠直觀碟渺,因為iOS
是小端模式鲜锚,所以是內存值是倒著突诬,使用x/4gx
打印出來的方便直接查看。
通過打印芜繁,把對象存儲的值都直觀的輸出了旺隙, 而且在0x000000170000414d
這個值里存儲了對象里屬性三個值,這就是屬性重排
骏令,雖然對象編譯到底層是結構體
蔬捷,按照結構體對齊,會極大的浪費空間 榔袋,蘋果在這一層又做了優(yōu)化周拐,就是屬性重排
,達到優(yōu)化空間的目的。利用空間換時間