iOS OC 对象原理探索二
上一篇提到alloc开辟内存是8字节对齐,用空间换取查找时间,那么像
int
、char
这样非8字节的变量,在内存中是什么样的呢?
首先我们看看对齐原则如下:
内存对齐原则
1. 数据成员对齐规则:
结构体(struct)或(联合体union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的
起始位置要从该成员大小或者成员的子成员大小的整数倍开始(只有该成员有子成员,比如数组、结构体等),
例如:int为4字节,则要从4的整数倍地址开始存储
2. 结构体作为成员:
如果一个结构体里有某些结构体成员,则结构体成员要从其中内部最大元素大小的整数倍地址开始存储.
例:(struct a 中存有 struct b, b中有char,int,double等元素,那么b应该从8的整数倍开始存储)
3. 收尾工作:
结构体的总大小,也就是sizeof的结构,必须是起内部最大的成员整数倍,不足的要补齐
接下来看一下在结构体
和结构体
嵌套中每个变量所占内存的分析,分析代码如下:
struct LGStruct1 {
char a; // 1 + 7 min(0 1) 0
double b; // 8 min(1 8) (1-7填充) 8 9 10 11 12 13 14 15
short d; // 2 + 2 min(16 2) 16 17
int c; // 4 min(18 4) (18 19填充) 20 21 22 23 -- 24
} MyStruct1;
struct LGStruct2 {
double b; // 8 0-7
int c; // 4 min(8 4) 8 9 10 11
char a; // 1 min(12 1) 12
short d; // 2 min(13 2) (13填充) 14 15 -- 16
struct LGStruct1 r; // min(16 24) struct里最大double 8的倍数
} MyStruct2; // struct里最大double 8,从8的倍数地址开始存储
// NSLog(@"%lu-%lu",sizeof(MyStruct1),sizeof(MyStruct2));
// 输出 24-40
struct LGStruct3 {
double b; // 8 0-7
int c; // 4 min(8 4) 8 9 10 11
char a; // 1 min(12 1) 12
struct LGStruct1 r; // min(13 24) (13 14 15)
// 16 - 39 struct里最大double 8的倍数
short d; // 2 min(40 2) 40 41 -- 48
} MyStruct3; // struct里最大double 8 补齐为48
// 结构体的总大小,也就是sizeof的结构,必须是起内部最大的成员整数倍,不足的要补齐
// 输出 24-48
根据上面代码打印分析,我们很容易分析出 结构体struct
和 结构体struct
嵌套所占的内存,完全遵守内存对齐原则。
成员变量内存探究
上一篇文章我们分析alloc源码得知,对象开辟内存空间是8的倍数(8字节对齐),最少占用16字节(16 - 8 > 16),那么系统真的会按照我们的意愿开辟内存么?下面我们来分析一下:
1.代码分析
LGTeacher *p = [[LGTeacher alloc] init];
p.name = @"Cooci";
p.age = 18;
p.height = 185;
p.hobby = @"女";
p.sex = 2;
p.ch1 = 'a';
p.ch2 = 'b';
打印分析如下:
由此可以看出,对象的属性并未按照每个属性8字节来开辟内存的,而是按照开篇所说的内存对齐原则来分配的,那么我们分析一下代码:
LGTeacher *p = [LGTeacher alloc];
// isa ---- 8
p.name = @"LG_Cooci"; // 8
p.age = 18; // 4
p.height = 185; // 8
p.hobby = @"女"; // 8
NSLog(@"%lu - %lu",class_getInstanceSize([p class]),malloc_size((__bridge const void *)(p)));
应该占40个字节,那么实际系统开辟的内存打印如下:
实际开辟了48内存,对象申请的内存的大小 和 系统开辟的大小并 不一致,接下来我们分析一下calloc
源码分析,看一下系统是怎么开辟内存的
2.calloc源码分析
通过源码分析,流程如下:
关键代码:
static MALLOC_INLINE size_t
segregated_size_to_fit(nanozone_t *nanozone, size_t size, size_t *pKey)
{
// size = 40
size_t k, slot_bytes;
if (0 == size) {
size = NANO_REGIME_QUANTA_SIZE; // Historical behavior
}
// 40 + 16-1 >> 4 << 4
// 40 - 16*3 = 48
//
// 16
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;
}
由此看出:
对象开辟内存是16字节对齐,是参照整个当前对象;前面8字节对齐,是参照对象里面的属性。
16字节对齐,系统开辟内存,避免风险,防止内存溢出,属性8字节内存对齐也有一定多余的空间 不确定属性具体排列顺序,不确定末尾是否有