一. 結(jié)構(gòu)體引入
結(jié)構(gòu)體是由基本數(shù)據(jù)類型或者指針,數(shù)組,結(jié)構(gòu)體/聯(lián)合體/枚舉等類型組成的一種結(jié)構(gòu),我們可以簡單地定義一個(gè)結(jié)構(gòu)體如下
struct Person {
char *contact;
bool sex;
short int age;
int height;
char name[9];
};
二. 結(jié)構(gòu)體所占內(nèi)存大小
結(jié)構(gòu)體的大小主要與操作系統(tǒng)地址總線寬度
碳柱、編譯器
、對齊方式
相關(guān)熬芜。
各種數(shù)據(jù)類型在ios中的占用內(nèi)存大小
在64位系統(tǒng)下探索結(jié)構(gòu)體占用的內(nèi)存大小
struct Person stu;
stu.sex = NO;
stu.age = 18;
stu.contact = "984361822@qq.com";
stu.height = 20;
stu.name[0]= 65;
stu.name[1]= 66;
stu.name[2]= 65;
stu.name[3]= 66;
stu.name[4]= 65;
stu.name[5]= 66;
stu.name[6]= 65;
stu.name[7]= 66;
stu.name[8]= 65;
printf("%lu",sizeof(stu)); //32
上面代碼打印出的stu所占內(nèi)存為32個(gè)字節(jié),
首先將斷點(diǎn)設(shè)置在如圖位置,當(dāng)斷點(diǎn)斷住時(shí),在調(diào)試區(qū)域可以看到stu對象,右擊stu,選中View Memory of "stu",可以查看stu的內(nèi)存分配
從內(nèi)存地址中的值,可以觀察到stu的內(nèi)存分配:
成員變量在內(nèi)存中的存儲位置為:
contact
; 是個(gè)指針,在64位系統(tǒng)下占用8個(gè)字節(jié)
sex,age,height
共同占用8個(gè)字節(jié)
sex;實(shí)際占用一個(gè)字節(jié),但需要字節(jié)對齊,sex后會(huì)空一個(gè)字節(jié)
age 占用2個(gè)字節(jié),
height;占用8個(gè)字節(jié)name
實(shí)際占用9個(gè)字節(jié)
,但會(huì)字節(jié)對齊,其后會(huì)預(yù)留7個(gè)字節(jié)
,以達(dá)到8字節(jié)的對齊
平臺原因(移植原因)
:不是所有的硬件平臺都能訪問任意地址上的任意數(shù)據(jù)的;某些硬件平臺只能在某些地址處取某些特定類型的數(shù)據(jù)涎拉,否則拋出硬件異常瑞侮。性能原因
:數(shù)據(jù)結(jié)構(gòu)(尤其是棧)應(yīng)該盡可能地在自然邊界上對齊。原因在于曼库,為了訪問未對齊的內(nèi)存区岗,處理器需要作兩次內(nèi)存訪問;而對齊的內(nèi)存訪問僅需要一次訪問毁枯。
三. 結(jié)構(gòu)體嵌套的內(nèi)存大小
struct Age {
int age;
bool sex;
};
struct Human {
char name;
struct Age a1;
};
結(jié)構(gòu)體Age的占用的內(nèi)存大小在64位系統(tǒng)下是8個(gè)字節(jié)慈缔,占用內(nèi)存最大的成員變量是age,占用4個(gè)字節(jié);
結(jié)構(gòu)體Human的尺寸在64位系統(tǒng)下占用12個(gè)字節(jié),由于結(jié)構(gòu)體Age中最大的成員變量是age,占用4個(gè)字節(jié);所以name占用一個(gè)字節(jié)后,將空三個(gè)自己,然后再開始存儲a1;
- 數(shù)據(jù)成員對?規(guī)則:結(jié)構(gòu)(struct)(或聯(lián)合(union))的數(shù)據(jù)成員,第一個(gè)數(shù)據(jù)成員放在offset為0的地方种玛,以后每個(gè)數(shù)據(jù)成員存儲的起始位置要從該成員大小或者成員的子成員大小(只要該成員有子成員藐鹤,比如說是數(shù)組,結(jié)構(gòu)體等)的整數(shù)倍開始
比如int為4字節(jié),則要從4的整數(shù)倍地址開始存
儲瓤檐。
min(當(dāng)前開始的位置m,n)m=9n=4 其中 m表示當(dāng)前成員的開始位置, n表示當(dāng)前成員所需要的位數(shù)。如果滿足條件 m 整除 n (即 m % n == 0), n 從 m 位置開始存儲, 反之繼續(xù)檢查 m = m+1 能否整除 n, 直到可以整除, 從而就確定了當(dāng)前成員的開始位置娱节。
數(shù)據(jù)成員為結(jié)構(gòu)體:當(dāng)結(jié)構(gòu)體嵌套了結(jié)構(gòu)體時(shí)挠蛉,作為數(shù)據(jù)成員的結(jié)構(gòu)體的自身長度作為外部結(jié)構(gòu)體的
最大成員的內(nèi)存大小
,比如結(jié)構(gòu)體a嵌套結(jié)構(gòu)體b肄满,b中有char谴古、int、double等稠歉,則b的自身長度為8最后結(jié)構(gòu)體的內(nèi)存大小必須是結(jié)構(gòu)體中
最大成員內(nèi)存大小
的整數(shù)倍
掰担,不足的需要補(bǔ)齊
案例分析:
四. OC類的內(nèi)存優(yōu)化
定義一個(gè)自定義CJLPerson類,并定義幾個(gè)屬性怒炸,
@interface LGPerson : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *nickName;
@property (nonatomic, assign) int age;
@property (nonatomic, assign) long height;
@property (nonatomic) char c1;
@property (nonatomic) char c2;
@end
@implementation LGPerson
@end
在OC類中聲明的實(shí)體屬性會(huì)轉(zhuǎn)化為數(shù)據(jù)成員带饱。每個(gè)OC類中還會(huì)有一個(gè)隱式的數(shù)據(jù)成員isa,這是一個(gè)指針類型的數(shù)據(jù)成員阅羹,并且是作為類的第一個(gè)數(shù)據(jù)成員被定義勺疼。
OC類中定義的實(shí)例屬性,系統(tǒng)在編譯時(shí)會(huì)創(chuàng)建一個(gè)帶下劃線的數(shù)據(jù)成員,屬性數(shù)據(jù)成員的內(nèi)存排列順序會(huì)被優(yōu)化處理捏鱼。
存儲順序?yàn)? isa,c1,c2执庐,age,name,nickname,height
直接定義內(nèi)部的數(shù)據(jù)成員不參與排序優(yōu)化
,比如下面的形式:
@implementation LGPerson {
//內(nèi)部的數(shù)據(jù)成員
int type;
NSString *sencondName;
}
@end
上面的實(shí)現(xiàn)中定義了兩個(gè)內(nèi)部數(shù)據(jù)成員type,sencondName穷躁。當(dāng)出現(xiàn)這種情況時(shí)編譯器不會(huì)對這些內(nèi)部數(shù)據(jù)成員的順序進(jìn)行優(yōu)化耕肩,而是按定義的順序在內(nèi)存中進(jìn)行排列,并且是優(yōu)先于屬性數(shù)據(jù)成員進(jìn)行排列,type和sencondName排列在isa之后,其他實(shí)例屬性之前
.
存儲順序?yàn)? isa,type,sencondName,c1,c2问潭,age,name,nickname,height
注意:type會(huì)占用8個(gè)字節(jié)
五. iOS中獲取內(nèi)存大小的三種方式
-
sizeof
:操作符
,傳入的可以是數(shù)據(jù)類型和對象,編譯階段就可以確定其大小 -
class_getInstanceSize
:用于獲取類的實(shí)例對象所占用的內(nèi)存大小,并返回具體的字節(jié)數(shù)
婚被,其本質(zhì)就是獲取實(shí)例對象中成員變量的內(nèi)存大小,obj4中會(huì)進(jìn)行8字節(jié)對齊
/**
* Returns the size of instances of a class.
*
* @param cls A class object.
*
* @return The size in bytes of instances of the class \e cls, or \c 0 if \e cls is \c Nil.
*/
OBJC_EXPORT size_t
class_getInstanceSize(Class _Nullable cls)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
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());
}
static inline uint32_t word_align(uint32_t x) {
//8字節(jié)對齊 #define WORD_MASK 7UL
return (x + WORD_MASK) & ~WORD_MASK;
}
-
malloc_size
:獲取系統(tǒng)實(shí)際分配的內(nèi)存大小
,64位系統(tǒng)下會(huì)進(jìn)行16字節(jié)對齊
六. 內(nèi)存對齊
6.1#pragma pack(n)
每個(gè)平臺上的編譯器都有自己的默認(rèn)“對齊系數(shù)
”(也叫對齊模數(shù))狡忙。程序員可以通過預(yù)編譯命令#pragma pack(n)
,n=1,2,4,8,16來改變這一系數(shù)址芯,在ios中灾茁,Xcode默認(rèn)為#pragma pack(8),即8字節(jié)對齊
通過預(yù)編譯命令#pragma pack()
取消自定義字節(jié)對齊方式
#pragma pack(1) //編譯器1字節(jié)對齊存儲
struct Person
{
char sex;
short age;
float weight;
char c1
};
#pragma pack() //取消1字節(jié)對齊谷炸,恢復(fù)默認(rèn)對齊
//sizeof(Person) = 8
struct Human
{
char sex;
short age;
float weight;
char c1;
};
//sizeof(Human) = 12
6.2 __attribute__((aligned (n)))
__attribute__((aligned (n)))
北专,讓所作用的結(jié)構(gòu)成員對齊在n字節(jié)自然邊界上。如果結(jié)構(gòu)中有成員的長度大于n旬陡,則按照最大成員的長度來對齊拓颓。
__attribute__((packed))
,取消結(jié)構(gòu)在編譯過程中的優(yōu)化對齊描孟,按照實(shí)際占用字節(jié)數(shù)進(jìn)行對齊驶睦。
#define PACKED __attribute__((packed))
struct PACKED Person {//取消字節(jié)對齊
char sex;
short age;
float weight;
};
#pragma pack() //恢復(fù)默認(rèn)對齊
//sizeof(Person) = 7 ,其中sex占一個(gè)字節(jié)砰左,age占兩個(gè)字節(jié),weight占4個(gè)字節(jié)
#define PACKED8 __attribute__((aligned (8)))
struct PACKED8 Student{
char sex;
short age;
float weight;
char c1;
};
#pragma pack() //恢復(fù)默認(rèn)對齊
//sizeof(Student) = 16 其中sex占一個(gè)字節(jié)场航,空隙1個(gè)字節(jié)缠导,age占兩個(gè)字節(jié),weight占4個(gè)字節(jié)溉痢,c1占一個(gè)字節(jié)僻造,末尾空隙7個(gè)字節(jié)
struct Human{
char sex;
short age;
float weight;
char c1;
};
//sizeof(Human) = 12 ,其中sex占一個(gè)字節(jié),空隙1個(gè)字節(jié)孩饼,age占兩個(gè)字節(jié)髓削,weight占4個(gè)字節(jié),c1占一個(gè)字節(jié)捣辆,末尾空隙三個(gè)字節(jié)
可以定義一個(gè)Student對象來斷點(diǎn)查看下內(nèi)存:
struct Student stu;
stu.sex = 'M';
stu.age = 18;
stu.weight= 50.0;
stu.c1 = 'A';
內(nèi)存的存儲情況如下:sex占一個(gè)字節(jié)蔬螟,空隙1個(gè)字節(jié),age占兩個(gè)字節(jié)汽畴,weight占4個(gè)字節(jié)旧巾,c1占一個(gè)字節(jié),末尾空隙7個(gè)字節(jié)
[圖片上傳失敗...(image-b91ff4-1600327993148)]
6.3 16字節(jié)對齊算法1
static inline size_t align16(size_t x) {
return (x + size_t(15)) & ~size_t(15);
}
6.4 16字節(jié)對齊算法2
#define SHIFT_NANO_QUANTUM 4
#define NANO_REGIME_QUANTA_SIZE (1 << SHIFT_NANO_QUANTUM) // 16
static MALLOC_INLINE size_t
segregated_size_to_fit(nanozone_t *nanozone, size_t size, size_t *pKey)
{
size_t k, slot_bytes;
if (0 == size) {
size = NANO_REGIME_QUANTA_SIZE; // Historical behavior
}
k = (size + NANO_REGIME_QUANTA_SIZE - 1) >> SHIFT_NANO_QUANTUM; // round up and shift for number of quanta
slot_bytes = k << SHIFT_NANO_QUANTUM; // multiply by power of two quanta size
*pKey = k - 1; // Zero-based!
return slot_bytes;
}
6.5 8字節(jié)對齊算法
static inline uint32_t word_align(uint32_t x) {
// 8字節(jié)對齊 #define WORD_MASK 7UL
return (x + WORD_MASK) & ~WORD_MASK;
}