【規則一】數據成員的對齊規則能夠理解爲min(m, n) 的公式, 其中 m表示當前成員的開始位置, n表示當前成員所須要的位數。若是知足條件 m 整除 n (即 m % n == 0), n 從 m 位置開始存儲, 反之繼續檢查 m+1位置 可否整除 所需的位數n, 直到能夠整除, 從而就肯定了當前成員的開始位置。markdown
【規則二】數據成員爲結構體:當結構體嵌套告終構體時,做爲數據成員的結構體的自身長度做爲外部結構體的最大成員的內存大小,好比結構體a嵌套結構體b,b中有char、int、double等,則b的自身長度爲8佈局
【規則三】最後結構體的內存大小必須是結構體中最大成員內存大小的整數倍,不足的須要補齊。優化
上面的文本,比較枯燥,咱們用圖來說解 定義兩個結構體 structA 和 structB,咱們就用上面的規則來猜這兩個結構體在內存中如何開闢內存?內存佔用多少? 任咱們宰割的結構體先準備上atom
struct TestStructA {
double a; // 8 byte
int b; // 4 byte
char c; // 1 byte
short d; // 2 byte
} structA;
struct TestStructB {
double a; // 8 byte
char b; // 1 byte
int c; // 4 byte
short d; // 2 byte
} structB;
複製代碼
不清楚OC的基本數據類型在32位和64位上佔用的,能夠參考下面的表格 spa
先看structA3d
struct TestStructA {
double a; // 8 byte
int b; // 4 byte
char c; // 1 byte
short d; // 2 byte
} structA;
複製代碼
一、a :根據規則1,structA的第一個成員a是double類型,偏移從0開始,a佔用8個字節,range:【0,8】,以下: 指針
二、b 的內存佔用,b是int類型,佔4個字節,接着a從地址 8 開始,而8%4==0,七號起始地址能夠整除當前要存的b的大小,因此b從8開始,佔4個字節,range:【8,12】,以下: code
三、c 是char,須要一個字節,繼續b從地址12開始,12%1==0,因此c從12開始,佔一個字節,range:【12,13】,以下: orm
四、終於輪到了d,繼續c以後從13開始,d是short須要2個字節,可是13%2!=0 , 那就繼續下一個位置14開始,14%2!=0, OK,咱們就從14開始存,佔2個字節,range:【14,16】,以下: 對象
咱們的c和d之間空了一個位置,也就是13,這個位置系統會自動補0.
根據規則三,當所有安排穩當,結構體的總大小是否是最大成員的整數倍,也就是整個結構體size % 最大成員的size == 0 可成立,若是不能整除,補0直到能夠整除最大成員的size。structA很顯然,剛恰好 16byte,最大成員a須要8byte,能夠整除,因此,第4步就是structA的內存圖示。
咱們來驗證一下,structA是否是佔用了16個字節
再看structB
struct TestStructB {
double a; // 8 byte
char b; // 1 byte
int c; // 4 byte
short d; // 2 byte
} structB;
複製代碼
一、a須要8個字節,從0位置開始,和structA同樣: 二、b是char類型,須要一個字節,此時應該從位置8繼續, 位置8 % b所需的1個字節 == 0,因此,從位置8開始,佔一個位置,range:【8,9】
三、c是int,須要4個字節的位置,此時繼續從9開始,顯然9不能整除4,繼續下一個位置10......直到12能夠整除4,因此c從位置12開始,range:【12,16】
細心的朋友會發現:structA到位置16就已經結束了,structB的d還沒開始計算。 四、d是short,須要2個字節,此時位置繼續從16開始,16 % 2 == 0, 因此能夠從位置16開始開闢,須要2個字節,因此range:【16,18】
這就結束了?????顯然不是!structB總大小18 明顯不能整除 最大成員a所需的8個字節,直到補0到位置24,才能夠整除8。
最後,structB實際須要24個字節,咱們來驗證一下
structA和structB看似同樣的寫法,所需的內存空間倒是大不相同 因此,瞭解內存對齊,是開發者的一項基本功。
struct TestStructC {
char *name;
int age;
} structC;
複製代碼
先查看結果他所佔用的字節:
16個字節!
其實很好理解,char雖然只有一個字節,可是 name是個指針變量 ,指針做爲一個變量,須要 8個字節 。name指向的具體內容纔是1個字節。 因此在structC裏,name指針變量纔是成員。
固然了,最後檢查一遍:整個結構體的size是否是最大成員size的整數倍!不是,要補全
引伸:對象的大小呢? 咱們在開發時,會定義 LYPerson *person = [LYPerson.alloc init];
本質上,person是一個指針,指向了這個對象在內存中的實際位置。 因此當咱們sizeof(person)
的時候,實際上,是得到這個指針變量的大小!指針須要8個字節!和person的屬性內容無關!
定義了這麼一個類
@interface LYPerson : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;
@property (nonatomic) char c1;
@property (nonatomic) char c2;
@property (nonatomic) char c3;
@property (nonatomic) char c4;
@end
複製代碼
初始化,先不給屬性賦值
LYPerson *person = [LYPerson.alloc init];
複製代碼
咱們來看一看person對象的內存分佈 經過po person
查看內存地址 而後使用 x/4gx 內存地址
、x/8gx 內存地址
分別查看4個數量和8個數量,每一個分佈須要8個字節,每一行有2個分佈,也就是16字節。
每一行,如0x600000d72500: 0x00000001099a87a8 0x0000000000000000
表示屬性在內存中的地址,一行共16個字節。 咱們打印看一下0x00000001099a87a8
,只有他不爲空
0x00000001099a87a8
地址裏的值是類名!一行總共16個字節,他就佔了8個字節。
一、咱們開始給屬性賦值,並打上斷點,先給name賦值,並查看內存變化:
LYPerson *person = [LYPerson.alloc init];
person.name = @"一草一晚上一孤城";
person.age = 27;
person.c1 = 'a'; // ascii碼 97
person.c2 = 'b'; // ascii碼 98
person.c3 = 'A'; // ascii碼 65
person.c4 = 'B'; // ascii碼 66
複製代碼
在0x6000029aad70
出開始的16個字節區域內,多了一個子偏移地址:0x000000010d4e9038
,裏面存儲值是屬性name
的值一草一葉一孤城
。
二、斷電繼續往下走,給age賦值,並查看內存變化:
有一個內存地址變成了0x1b,它裏面存儲了age
的內容 27
三、斷點繼續往下走,給c1賦值: 一樣是在
0x6000029aad60
區域,多了一個內存地址0x61,值是c1的‘a’。 四、斷點往下走,給c2賦值: 它是緊挨着c1的地址0x61開始存,他是0x62,說明什麼,char類型只佔1個字節!
五、繼續往下走,給c3賦值:
六、斷點繼續走,給c4賦值:
完成了,最終,成了這樣: 分別存儲了
name、age、c一、c二、c三、c4
,可是,卻不是按咱們賦值的順序存放,這是由於,蘋果爲咱們作了字節重排優化!