知識點須知
C和OC各數(shù)據(jù)類型在32位和64位CPU中的占用的字節(jié)大小列表:
| C | OC | 32位 | 64位 |
|---|---|---|---|
| bool | BOOL(64位) | 1 | 1 |
| signed char | (_ _signed char)int8_t、BOOL(32位) | 1 | 1 |
| unsigned char | Boolean | 1 | 1 |
| short | int16_t | 2 | 2 |
| unsigned short | unichar | 2 | 2 |
| int int32_t | NSInterger(32位)、boolean_t(32位) | 4 | 4 |
| unsigned int | boolean_t(64位)、NSUInteger(32位) | 4 | 4 |
| long | NSInteger(64位) | 4 | 8 |
| unsigned long | NSUInteger(64位) | 4 | 8 |
| long long | int64_t | 8 | 8 |
| float | CGFloat(32位) | 4 | 4 |
| double | CGFloat(64位) | 8 | 8 |
對象的內(nèi)存大小與屬性重排
-
class_getInstanceSize
??首先我們來看一下Runtime函數(shù)class_getInstanceSize獲取實例的大小。
其底層源碼是:
size_t class_getInstanceSize(Class cls)
{
if (!cls) return 0;
return cls->alignedInstanceSize();
}
類的屬性大小,采用8字節(jié)對齊原則
// Class's ivar size rounded up to a pointer-size boundary.
uint32_t alignedInstanceSize() {
return word_align(unalignedInstanceSize());
}
# define WORD_MASK 7UL
static inline uint32_t word_align(uint32_t x) {
return (x + WORD_MASK) & ~WORD_MASK;
}
發(fā)現(xiàn)取決于data()->ro->instanceSize,即是對象bits在運行時的class_rw_t的ro,只讀內(nèi)存的實例大小instanceSize,其實在編譯期就定了,在運行時,class_rw_t的存在(因為其實可讀可寫的)就決定可以動態(tài)的添加方法協(xié)議,動態(tài)關(guān)聯(lián)屬性。
// May be unaligned depending on class's ivars.
uint32_t unalignedInstanceSize() {
assert(isRealized());
return data()->ro->instanceSize;
}
小結(jié):從源碼可以知道,對象的大小與屬性有關(guān)
- 驗證對象大小與屬性相關(guān)
??新建一個類,沒有任何屬性,看看創(chuàng)建出來的對象大小是多少
@interface PSYPerson : NSObject
///** */
//@property (nonatomic,copy) NSString *name;
///** */
//@property (nonatomic,assign) NSInteger age;
///** */
//@property (nonatomic,copy) NSString *nickName;
@end
....
PSYPerson *psy1 = [[PSYPerson alloc] init];
PSYPerson *psy2 = [[PSYPerson alloc] init];
NSLog(@"psy1對象大?。?lu",class_getInstanceSize(psy1.class));
NSLog(@"psy2對象大?。?lu",class_getInstanceSize(psy2.class));
輸出結(jié)果:

從結(jié)果可以看到,為什么是8呢,明明沒有任何屬性?這里首先說明,對象默認(rèn)繼承自obj_object結(jié)構(gòu)體,默認(rèn)繼承了一個isa指針占8個字節(jié)。
給類添加屬性@property (nonatomic,copy) NSString *name;,重新運行程序,查看打印結(jié)果:

與我們預(yù)想的一樣,為了進一步驗證,添加如下屬性(跑在6s真機上測試,根據(jù)上面知識點須知 C和OC不同數(shù)據(jù)類型在32位和64位CPU中占有字節(jié)大小列表):
@interface PSYPerson : NSObject
/** 8字節(jié) */
@property (nonatomic,copy) NSString *name;
/** 4字節(jié) */
@property (nonatomic,assign) int age;
/** 1字節(jié) */
@property (nonatomic) char msg;
/** 1字節(jié) */
@property (nonatomic) BOOL isWorking;
@end
分析:8字節(jié)+4字節(jié)+1字節(jié)+1字節(jié) = 14字節(jié)
再加上isa指針8字節(jié) :14字節(jié)+8字節(jié) = 22字節(jié),根據(jù)8字節(jié)對齊原則應(yīng)該是24字節(jié)

源碼說只與類的ivars有關(guān),那方法對其是否有影響呢?筆者有添加了一個類方法一個對象方法,繼續(xù)輸出印證,發(fā)現(xiàn)還是24字節(jié);

- 屬性重排
對象的屬性在內(nèi)存中是怎樣存在的呢?
因為屬性是有32字節(jié),筆者特意打印了8段數(shù)據(jù),實際對象的地址是從0x2807e05c0開始,占32個字節(jié),也就是下圖中紅框起來的數(shù)據(jù)區(qū),可以知道,在對象被創(chuàng)建出來的時候?qū)傩员荒J(rèn)賦值為 0 或則 nil ,其他的是臟數(shù)據(jù)。

賦值之后,重新打印查看內(nèi)存數(shù)據(jù):

可發(fā)現(xiàn)不管屬性順序是怎樣,先賦值哪一個,運行的時候,蘋果會對對象的屬性進行重排,使其內(nèi)存優(yōu)化。
總結(jié)
- 對象屬性內(nèi)存采用
8字節(jié)對齊 - 對象
大小與ivars有關(guān),與方法無關(guān) - 蘋果會進行
屬性重排,以優(yōu)化內(nèi)存
結(jié)構(gòu)體內(nèi)存對齊
??上面研究了對象內(nèi)存對齊,對于結(jié)構(gòu)體是什么樣的呢?研究結(jié)構(gòu)體其實是有很大的意義,因為在底層探索過程中,基本上90%以上都是結(jié)構(gòu)體共用體。
struct PSYStruct1 {
double a; // 8
char b; // 1
int c; // 4
short d; // 2
}struct1;
struct PSYStruct2 {
double a; // 8
int b; // 4
char c; // 1
short d; // 2
}struct2;
估計很大一部分人認(rèn)為,這兩個結(jié)構(gòu)體一抹抹一樣樣,大小都是16字節(jié),可是運行結(jié)果卻讓人大跌眼鏡:


結(jié)構(gòu)體內(nèi)存對齊
-
數(shù)據(jù)成員對齊規(guī)則:結(jié)構(gòu)體(struct)或聯(lián)合體/共用體(union)的數(shù)據(jù)成員,第一個數(shù)據(jù)成員放在offset為0??的地方,以后每個數(shù)據(jù)成員存儲的起始位置要從該成員大小或者成員的子成員大小(只要該成員有子成員,比如說是數(shù)組,結(jié)構(gòu)體)的整數(shù)倍開始-
eg:比如int為4字節(jié),則要從4的整數(shù)倍地址開始存儲
-
-
結(jié)構(gòu)體作為成員:如果一個結(jié)構(gòu)里有某些結(jié)構(gòu)體成員,則結(jié)構(gòu)體成員要從其內(nèi)部最大元素大小的整數(shù)倍地址開始存儲。-
eg:struct a里存有struct b,b里有char,int,double等元素,那b應(yīng)該從8的整數(shù)倍開始存儲)
-
規(guī)則三:結(jié)構(gòu)體的總大小,也就是sizeof的結(jié)果,必須是其內(nèi)部最大成員的整數(shù)倍,不足的要補齊。
根據(jù)數(shù)據(jù)成員對齊規(guī)則:
double a; ? // 8 從0開始存8位 也就是 [0 , 7]
char b; ? ?// 1 [8]
int c; ? ? // 4 (9, 10, 11, [12, 13, 14, 15] 從4的整數(shù)倍開始存,也就是12開始
short d; ? // 2 [16, 17] 17個字節(jié),但是根據(jù)規(guī)則三,大小為最大成員8的整數(shù)倍取24字節(jié)
同理
double a; // 8 從0開始存8位 也就是 [0 , 7]
int b; ? // 4 [8, 9, 10, 11] 因為8是4的整數(shù)倍,因此從8開始存
char c; ? // 1 [12]
short d; ? // 2 [14, 15] 根據(jù)規(guī)則三,大小為最大成員8的整數(shù)倍所以總共16個字節(jié)
結(jié)構(gòu)體嵌套情況
struct PSYStruct3 {
double a;
int b;
char c;
short d;
int e;
struct PSYStruct1 str;
}struct3;
double a; ? // 8字節(jié) [0 , 7]
int b; ? ? // 4字節(jié) [8, 9, 10, 11]
char c; ? ? // 1字節(jié) [12]
short d; ? ? // 2字節(jié) (13, [14, 15]
int e; ? ? ? // 4字節(jié) [16, 17, 18, 19]
struct PSYStruct1 str;// (20, 21, 22, 23, [24, 47]
根據(jù)規(guī)則結(jié)構(gòu)體作為成員:,str結(jié)構(gòu)體開始存儲的位置是str結(jié)構(gòu)體內(nèi)部最大元素的大小8的整數(shù)倍24開始,占24個字節(jié),就是到47
所以整個結(jié)構(gòu)體占48字節(jié)。
