前言
在 2013 年 9 月,苹果推出了 iPhone5s,与此同时,iPhone5s 配备了首个采用 64 位架构的 A7双核处理器,为了节省内存和提高执行效率,苹果提出了Tagged Pointer的概念。对于 64 位程序,引入 Tagged Pointer 后,相关逻辑能减少一半的内存占用,以及3 倍的访问速度提升,100倍的创建、销毁速度提升。
问题
我们先看看原有的对象为什么会浪费内存?
假设我们要存储一个 NSNumber对象,其值是一个整数。正常情况下,如果这个整数只是一个 NSInteger的普通变量,那么它所占用的内存是与 CPU 的位数有关,在 32 位 CPU 下占 4个字节,在 64 位CPU 下是占 8 个字节的。而指针类型的大小通常也是与CPU位数相关,一个指针所占用的内存在 32 位 CPU 下为4 个字节,在 64 位 CPU 下也是8个字节。
所以一个普通的 iOS 程序,如果没有Tagged Pointer对象,从 32 位机器迁移到 64 位机器中后,虽然逻辑没有任何变化,但这种 NSNumber、NSDate 一类的对象所占用的内存会翻倍。如下图所示:
01
Tagged Pointer
为了改进上面提到的内存占用和效率问题,苹果提出了Tagged Pointer对象。由于 NSNumber、NSDate 一类的变量本身的值需要占用的内存大小常常不需要8个字节,拿整数来说,4 个字节所能表示的有符号整数就可以达到 20 多亿(注:2^31=2147483648,另外 1 位作为符号位),对于绝大多数情况都是可以处理的。
所以我们可以将一个对象的指针拆成两部分,一部分直接保存数据,另一部分作为特殊标记,表示这是一个特别的指针,不指向任何一个地址。所以,引入了Tagged Pointer对象之后,64 位 CPU 下NSNumber 的内存图变成了以下这样:
02
-
Tagged Pointer是专⻔⽤来存储⼩的对象,例如NSNumber,NSDate等。 -
Tagged Pointer指针的值不再是地址了,⽽是真正的值。所以,实际上它不再是⼀个对象了,它只是⼀个披着对象⽪的普通变量⽽已。所以,它的内存并不存储在堆中,也不需要malloc和free。 - 当指针不够存储数据时,就会使用动态分配内存的方式来存储数据。
- 在内存读取上有着
3倍的效率,创建时⽐以前快106倍。
总结
苹果将Tagged Pointer引入,给64位系统带来了内存的节省和运行效率的提高。Tagged Pointer通过在其最后一个 bit 位设置一个特殊标记,用于将数据直接保存在指针本身中。因为Tagged Pointer并不是真正的对象。
nonpointer
isa
nonpointer
-
0,代表普通的指针,存储着Class、Meta-Class对象的内存地址 -
1,代表优化过,使用位域存储更多的信息
has_assoc
- 是否有设置过关联对象,如果没有,释放时会更快
has_cxx_dtor
- 是否有
C++的析构函数(.cxx_destruct),如果没有,释放时会更快
shiftcls
- 存储着
Class、Meta-Class对象的内存地址信息
magic
- 用于在调试时分辨对象是否未完成初始化
weakly_referenced
- 是否有被弱引用指向过,如果没有,释放时会更快
deallocating
- 对象是否正在释放
extra_rc
- 里面存储的值是引用计数器减1
has_sidetable_rc
- 引用计数器是否过大无法存储在
isa中 - 如果为
1,那么引用计数会存储在一个叫SideTable的类的属性中
参考链接:巧神:
深入理解Tagged Pointer














网友评论