1. 內(nèi)存對齊原理
上一篇提到了對象在alloc
創(chuàng)建時內(nèi)存分配情況裹纳,
這一篇我們來更加深入的來研究一下什么叫內(nèi)存對齊
1.1 準備工作
話不多說,為了方便直觀的體現(xiàn)差異紧武,定義一個宏剃氧,打印對象的 isa、內(nèi)存地址阻星、對象類型占用的內(nèi)存大小
朋鞍、對象實際占用的內(nèi)存大小
、對象實際分配的內(nèi)存大小
妥箕。一定要注意占用跟實際分配的區(qū)別滥酥,是不一樣的。等一下看日志就一目了然畦幢。
照舊萬年祖?zhèn)饔^察對象日志代碼
#define Log(_var) ({ NSString *name = @#_var; NSLog(@"%@: %@ -> %p || 對象類型占用的內(nèi)存大小%lu, 對象實際占用的內(nèi)存大小%lu, 對象實際分配的內(nèi)存大小%lu ", name, [_var class], &_var, sizeof(_var), class_getInstanceSize([_var class]) ,malloc_size((__bridge const void*)(_var))); })
1.2 對象
昨天我們研究了人Person
類坎吻。今天不研究人,研究Dog
狗類??并且附上源代碼已經(jīng)運行結果日志
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <malloc/malloc.h>
@interface Dog : NSObject
@end
@implementation Dog
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Dog *dog = [Dog alloc];
Log(dog);
/*
* dog: Dog -> 0x7ffeefbff588 || 對象類型占用的內(nèi)存大小8, 對象實際占用的內(nèi)存大小8, 對象實際分配的內(nèi)存大小16
*/
}
return 0;
}
我們這個無屬性dog對象實際分配的內(nèi)存大小16跟我昨天說的一樣沒有騙大家宇葱。這一點可以放心瘦真。因為是無屬性對象實例刊头,該對象只有isa指針所以只占用了8個大小的內(nèi)存空間。
@property (nonatomic, strong) NSString *name;
如果我們給這個對象定義一個屬性時打印如下
對象類型占用的內(nèi)存大小8, 對象實際占用的內(nèi)存大小16, 對象實際分配的內(nèi)存大小16
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) int age;
如果我們給這個對象定義兩個屬性時打印如下
對象類型占用的內(nèi)存大小8, 對象實際占用的內(nèi)存大小24, 對象實際分配的內(nèi)存大小32
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) int age;
@property (nonatomic, assign) int height;
如果我們給這個對象定義三個屬性時打印如下
對象類型占用的內(nèi)存大小8, 對象實際占用的內(nèi)存大小24, 對象實際分配的內(nèi)存大小32
我們發(fā)現(xiàn)隨著屬性的增加實際的分配內(nèi)存大小符合昨天我們研究的對象創(chuàng)建16字節(jié)
對齊原則诸尽。
對象類型占用的內(nèi)存大小
始終為8我猜測是對象指針的占用大小
對象實際占用的內(nèi)存大小
屬性以8字節(jié)對齊排列
我們來給dog屬性賦值之后
Dog *dog = [Dog alloc];
dog.name = @"旺財";
dog.age = 2;
dog.height = 80;
x/4gx dog
一下看一下內(nèi)存情況
0x104007b50: 0x001d800100002315 0x0000000200000001
0x104007b60: 0x0000000100001018 0x0000000000000000
我們發(fā)現(xiàn) 0x0000000100001018
是設置的name
原杂, 0x0000005000000002
居然po不出來。
我們嘗試po 0x00000050
是height
弦讽,po 0x00000002
是age
。原來height
跟age
共用了內(nèi)存段節(jié)約內(nèi)存
1.3結論
以上這個例子來進行說明 蘋果中屬性重排
膀哲,即內(nèi)存優(yōu)化
因為age跟height各占用8比較浪費往产。雖然分配了32。但是一共占用24某宪。跟你定義屬性的順序無關仿村。最終會按著最優(yōu)的方式重新排列
2 結構體
對象研究完了兴喂。我們再來看一下結構體,看一下通過結構體的方式是否能驗證些什么畏鼓?
struct Dog1{
char a; //1字節(jié)
double b; //8字節(jié)
int c; //4字節(jié)
short d; //2字節(jié)
}Dog1;
struct Dog2{
double b; //8字節(jié)
int c; //4字節(jié)
short d; //2字節(jié)
char a; //1字節(jié)
}Dog2;
NSLog(@"狗一大小%lu--狗二大小%lu",sizeof(Dog1), sizeof(Dog2));
/*狗一大小24--狗二大小16*/
我們發(fā)現(xiàn)同是 4
個屬性云矫,同樣包含char
,double
,int
,short
4個類型汗菜,只是順序不同罷了。最后大小缺不一樣
莫非就是我們之前說的內(nèi)存對齊
?只是對象不需要你手動排列巡揍。而結構體需要你手動排列才生效腮敌,那么它的規(guī)則是什么呢俏扩?
內(nèi)存對齊規(guī)則
一般內(nèi)存對齊的原則主要有以下三點
1、數(shù)據(jù)成員的對齊規(guī)則可以理解為min(m, n)
的公式, 其中 m
表示當前成員的開始位置, n
表示當前成員所需要的位數(shù)啤斗。如果滿足條件 m
整除 n
(即 m % n == 0
), n 從 m 位置開始存儲, 反之繼續(xù)檢查 m + 1
能否整除n
, 直到可以整除, 從而就確定了當前成員的開始位置钮莲。
2、結構體作為成員:如果一個結構里有某些結構體成員,則結構體成員要從其內(nèi)部最大元素大小的整數(shù)倍地址開始存儲.(struct a里存有struct b,b里有char
,int
,double
等元素,那b應該從8
的整數(shù)倍開始存儲.)
3崔拥、結構體的總大小,也就是sizeof
的結果,.必須是其內(nèi)部最大成員的整數(shù)倍.不足的要補?
我們通過簡單的計算來驗證一下
結構體Dog1
內(nèi)存大小計算
根據(jù)內(nèi)存對齊規(guī)則計算Dog1的內(nèi)存大小链瓦,詳解過程如下:
變量 a
:占 1
個字節(jié),從0
開始渤刃,此時 min(0, 1)
卖子,即 0
存儲 a
變量 b
:占 8
個字節(jié)刑峡,從1
開始,此時 min(1, 8)
诫舅, 1
不能整除 8
宫患,繼續(xù)往后移動,知道 min(8, 8)
俏讹,從 8
開始泽疆,即 8-15
存儲 b
變量 c
:占4
個字節(jié)玲献,從16
開始,此時min(16, 4)
瓢娜,16
可以整除 4
眠砾,即 16-19
存儲 c
變量 d
:占2
個字節(jié)托酸,從20
開始柒巫,此時min(20, 2)
堡掏,20
可以整除 2
刨疼,即 20-21
存儲 d
因此 Dog1
的需要的內(nèi)存大小為 15
字節(jié),而 Dog1
中最大變量的字節(jié)數(shù)為 8
亭畜,所以 Dog1
實際的內(nèi)存大小必須是 8
的整數(shù)倍贱案,18
向上取整到 24
止吐,主要是因為 24
是 8
的整數(shù)倍侨糟,所以 sizeof(Dog1)
的結果是 24
結構體 Dog2
內(nèi)存大小計算
根據(jù)內(nèi)存對齊規(guī)則計算 Dog2
的內(nèi)存大小秕重,詳解過程如下:
變量 b
:占 8
個字節(jié),從 0
開始二拐,此時 min(0, 8)
凳兵,即 0-7
存儲 b
變量 c
:占 4
個字節(jié)庐扫,從 8
開始,此時 min(8, 4)
铅辞, 8
可以整除 4
,即 8-11
存儲 c
變量 d
:占 2
個字節(jié)斟珊,從 12
開始倍宾,此時 min(12, 2)
, 20
可以整除2高职,即 12-13
存儲 d
變量 a
:占 1
個字節(jié),從 14
開始寥粹,此時 min(14, 1)
埃元,即 14
存儲 a
因此Dog2
的需要的內(nèi)存大小為 15
字節(jié),而 Dog2
中最大變量的字節(jié)數(shù)為 8
阔拳,所以 Dog2
實際的內(nèi)存大小必須是 8
的整數(shù)倍类嗤, 15
向上取整到 16
,主要是因為 16
是 8
的整數(shù)倍货裹,所以 sizeof(Dog2)
的結果是 16
不知道誰最大弧圆?我們來先看一個表笔咽,下表是對不同數(shù)據(jù)類型所占用的內(nèi)存大小(純手打,可復制)
有需要的點個關注叶组。評論查眼??Mark
扶叉,老司機開車從此不迷路
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 | boolean_t(32位)溢十、NSInteger(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 |