我们在分析对象创建的流程时发现,对象在创建之前,第一步是先计算实例对象所占空间大小。所以我们今天来看一下是如何创建的。
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
我们在 lldb 里面将 x
与 WORD_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
@property (nonatomic, assign) char ch; // 1 byte
@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
是不是感觉自己棒棒哒 (๑•̀ㅂ•́)و✧