Person *person = [[Person alloc] init];
一开始的分析如下:
NSLog@(@"%zd".class_getInstanceSize([Person class])); // 打印==8
NSLog(@"%zd",malloc_size((__bridge const void *) person)); // 打印16
一个打印8一个打印16 那个才是person的真实的占用内存大小呢? 我们通过上篇文章分析的到oc对象的本质是一个结构体并且会继承父类NSObiect的isa,已知一个isa 的指针式并且占用8个字节?怀着疑问先找到了内存对齐的规则如下:
1.数组成员对齐规则: 结构体或联合体的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的其实位置要从该成员的大小或子成员大小(只要该成员有子成员,比如说是数组和结构体等)的整数倍开始
例如:
int 为 4 字节,则要从4的整数倍地址开始存储
假如:当前开始位置在8 则int的存储
则为:9 10 11 12
假如:开始位置为9
则为:10(空) 11(空) 12(为4的整数倍则开始存储) 13 14 15
2.结构体作为成员: 如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储
例如:
struct a {
double a_a, [0,7]
int a_b, [8]
char a_c, [9]
struct b, (10,11,12,13,14,15)16+16=32
}a1 32字节
struct b {
double b_a, //8 字节 [0,7]
int b_b, //4 字节 [8,9,10,11]
char c_c, //1 字节 [12]
}b1 16字节
结构体a 包含结构体b,结构体 b里边最大的是8字节 则存储开始位置应该从8的整数被开始
3.结构体的总大小,必须是其内部最大的整数倍,不足的需要补齐
iOS基本数据类型 字节对照表
在知晓内存对齐规则后结合我们之前分析能的到Person在**c++**代码中的实现应该如下:
结合内存对齐规则 我们可以暂时的出结论Person 所占用的内存是8字节 但是这个16是怎么来的我们需要翻阅一下iOS的源码看下能否得出结论Objc源码 需要编译
struct Person_IMP {
Class isa;
}
// 我们可以找到如下代码
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
int construct_flags = OBJECT_CONSTRUCT_NONE,
bool cxxConstruct = true,
size_t *outAllocatedSize = nil)
{
ASSERT(cls->isRealized());
// Read class's info bits all at once for performance
bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();
bool hasCxxDtor = cls->hasCxxDtor();
bool fast = cls->canAllocNonpointer();
size_t size;
// 我们可以看出这行代码就是在获取内存大小的
size = cls->instanceSize(extraBytes);
if (outAllocatedSize) *outAllocatedSize = size;
id obj;
if (zone) {
obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
} else {
// 这里是分配内存
obj = (id)calloc(1, size);
}
if (slowpath(!obj)) {
if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
return _objc_callBadAllocHandler(cls);
}
return nil;
}
if (!zone && fast) {
// 关联isa指针
obj->initInstanceIsa(cls, hasCxxDtor);
} else {
// Use raw pointer isa on the assumption that they might be
// doing something weird with the zone or RR.
obj->initIsa(cls);
}
if (fastpath(!hasCxxCtor)) {
return obj;
}
construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;
return object_cxxConstructFromClass(obj, cls, construct_flags);
}
inline size_t instanceSize(size_t extraBytes) const {
// 查阅资料的到这句话的意思是快速计算内存
// 在objc2 中基本上都是快速计算
if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
return cache.fastInstanceSize(extraBytes);
}
///
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
}
size_t fastInstanceSize(size_t extra) const
{
ASSERT(hasFastInstanceSize(extra));
if (__builtin_constant_p(extra) && extra == 0) {
return _flags & FAST_CACHE_ALLOC_MASK16;
} else {
size_t size = _flags & FAST_CACHE_ALLOC_MASK;
// remove the FAST_CACHE_ALLOC_DELTA16 that was added
// by setFastInstanceSize
// 总这里我们可以看出来iOS 内存分配内存需要16位对齐的
return align16(size + extra - FAST_CACHE_ALLOC_DELTA16);
}
}
结论: 从以上代码中我们可以看出iOS 对象内存分配是16位对齐的这也就解释了我们开始的问题.
// 获取NSObject 的成员变量所占的字节
NSLog@(@"%zd".class_getInstanceSize([Person class])); // 打印==8
// 获取该对象指针指向的内存大小
NSLog(@"%zd",malloc_size((__bridge const void *) person)); // 打印16
如今问题又来了 为什么需要16位对齐呢?
1.如果是8字节对齐,假设一下,我们创建任意对象都会有isa 指针一个isa 指针8个字节,也就是说多个没有属性的对象,那么它们的内存空间就会完全的挨在一起,容易混乱。从提高寻址效率和内存空间综合考虑。