Objective-C Object Model(3/4)

本文主要讨论Objective-C对象内存布局并与C++内存布局进行比较,最后讨论了Apple针对64位处理器引入的Tagged Poniters

©原创文章,转载请注明出处!

Objective-C对象内存布局


前文我们知道Objective-C的类、对象本质上都是C语言的struct,且第一个成员变量是指向元类的isa指针。

那么Objective-C对象在内存中的布局是怎样的?

可以看到对象的成员变量从根类开始依次排列(isa指针就是根类NSObject的成员变量)。

OK,我们可以验证一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@interface A : NSObject
{
NSInteger _a;
}

@end

@interface B : A
{
NSInteger _b;
}

@end

@interface C : B
{
NSInteger _c;
}

@end

结果:

呵呵,结果很清楚了!

我们来看看C++对象的内存布局:

对比这两个内存布局图,对于包含虚函数的C++对象来说,其在内存上的布局与Objective-C对象有几分神似,前者含有指向虚函数表的vfptr指针,后者含有指向元类的isa指针。

Tagged Pointer


我们知道指针在本质上就是一个整数,在32位系统上它就是一个32位的整数,那么在64位的系统上它自然就是一个64位的整数了。

为了跨平台和处理器的访问效率,一般要求内存中的数据以特定的字节对齐。以32位平台为例,若一个整数存储在以0x00000003起始的4字节内,则读取这个整数需要2个CPU周期,而如果该整数存储在以0x00000004起始的4字节内则只需1个CPU周期。

在IOS中,数据都是16字节对齐的,也就是指针的后4位都是0。

Apple从iPhone5s开始采用64-bit A7双核处理器,因此指针也扩展到64位,其寻址能力:2^64,远远超过目前移动设备的实际需要,最终导致指针的前半部分出现大量的0,造成内存空间的浪费。

此时,Tagged Pointer应运而生。

那么什么是Tagged Pointer呢?

简单地讲,如果一个指针存储的是对象本身,而不是对象的地址,那么它就是Tagged Pointer

对比一下普通指针和Tagged指针(来自WWDC2013 session404):

从Apple的介绍我们可以知道:

  • Tagged Poniter主要用于处理小型数据,如:NSNumberNSData

  • Tagged Pointer能节省内存提高效率(省去多余的allocate/destroy)

由于Tagged Pointer不再指向object,首当其冲的就是 isa 指针

最后一个问题,编译器怎么判断一个指针是否是Tagged Pointer

1
2
3
4
5
6
7
struct objc_object 
{
bool isTaggedPointer()
{
return ((uintptr_t)this & 1);
}
};

很明显,编译器利用了普通指针4字节对齐的特征。