对象实例的空间大小

我们在分析对象创建的流程时发现,对象在创建之前,第一步是先计算实例对象所占空间大小。所以我们今天来看一下是如何创建的。

talk is cheap, show me the code!我们就从代码中寻找我们的答案。

计算内存大小

size_t size = cls->instanceSize(extraBytes);

size_t instanceSize(size_t extraBytes) {
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
}

首先,在获取对象实例空间大小时,要求所有的对象至少为 16 字节,为什么是 16 字节呢?

// Class's ivar size rounded up to a pointer-size boundary.
uint32_t alignedInstanceSize() {
return word_align(unalignedInstanceSize());
}

static inline uint32_t word_align(size_t x) {
return (x + WORD_MASK) & ~WORD_MASK;
}
#ifdef __LP64__
# define WORD_SHIFT 3UL
# define WORD_MASK 7UL
# define WORD_BITS 64
#else
# define WORD_SHIFT 2UL
# define WORD_MASK 3UL
# define WORD_BITS 32
#endif

其次,对象的地址进行了一个 字节对齐 的操作。在64位架构上进行 8 字节对齐,32位机器上进行4字节对齐。字节对齐这段代码只有一行:

(x + WORD_MASK) & ~WORD_MASK;

我们来分析下这行代码:

WORD_MASK 是一个宏定义,那么就可以针对不同平台有不同的值,这里就是针对 64/32 位架构做了区分。

(x + WORD_MASK) & ~WORD_MASK, 我们用一个例子来说明,以8字节对齐为例:

uint32_t x = 25;
uint32_t wordMask = 7
uint32_t y = (x + wordMask) & ~wordMask;
NSLog(@"%d", y); //32

word_align.png

我们在 lldb 里面将 xWORD_MASK 都转化成二进制计算:

	27		011011
+
7 000111
=
31 100010
&
~7 111000
=
32 100000

字节对齐的操作之后,对象的空间大小是**8的倍数**,那么对象的起始地址也必定是8的倍数。

还有另外一种方法也能达到8字节对齐的效果,即

(x + WORD_MASK) >> 3 << 3

如果要符合不同平台,需要定义个宏定义,其实上面的宏定义里面已经定义好了,即

(x + WORD_MASK) >> WORD_SHIFT << WORD_SHIFT

这是一个例子

现在我们给出我们的一个类 SMPerson,计算一下他所占的内存空间:

@interface SMPerson : NSObject

@property (nonatomic, copy) NSString *name; // 8 bytes
@property (nonatomic, copy) NSString *address; // 8 bytes
@property (nonatomic, assign) int age; // 4 bytes
@property (nonatomic, assign) CGFloat height; // 8 bytes

@end

OC 类在底层实现还有一个隐藏的 isa 指针,这里如果不考虑成员变量在内存中是如何分布(至于这里为什么不需要考虑内存分布,是因为这部分编译器已经帮我们处理好了),计算出来的实例对象的大小为40字节,下面我们验证一下:

在 main.m 文件中

int main(int argc, const char * argv[]) {
@autoreleasepool {
SMPerson *person = [[SMPerson alloc] init];
person.name = @"CC";
person.age = 18;
person.address = @"上海";
person.height = 180;
NSLog(@"%lu", class_getInstanceSize([person class]));
}
return 0;
}

控制台输出:

2019-12-22 23:26:50.541712+0800 objc-debug[13935:27611066] 40

是不是感觉自己棒棒哒 (๑•̀ㅂ•́)و✧