iOS OC 对象原理探索二

上一篇提到alloc开辟内存是8字节对齐,用空间换取查找时间,那么像intchar这样非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字节内存对齐也有一定多余的空间  不确定属性具体排列顺序,不确定末尾是否有
发布了17 篇原创文章 · 获赞 9 · 访问量 170

猜你喜欢

转载自blog.csdn.net/LiangliangSpeak/article/details/105364830