Tagged Pointer 和 nonpointer

前言

20139 月,苹果推出了 iPhone5s,与此同时,iPhone5s 配备了首个采用 64 位架构的 A7双核处理器,为了节省内存和提高执行效率,苹果提出了Tagged Pointer的概念。对于 64 位程序,引入 Tagged Pointer 后,相关逻辑能减少一半的内存占用,以及3 倍的访问速度提升,100倍的创建、销毁速度提升。

问题

我们先看看原有的对象为什么会浪费内存?

假设我们要存储一个 NSNumber对象,其值是一个整数。正常情况下,如果这个整数只是一个 NSInteger的普通变量,那么它所占用的内存是与 CPU 的位数有关,在 32CPU 下占 4个字节,在 64CPU 下是占 8 个字节的。而指针类型的大小通常也是与CPU位数相关,一个指针所占用的内存在 32CPU 下为4 个字节,在 64CPU 下也是8个字节。

所以一个普通的 iOS 程序,如果没有Tagged Pointer对象,从 32 位机器迁移到 64 位机器中后,虽然逻辑没有任何变化,但这种 NSNumberNSDate 一类的对象所占用的内存会翻倍。如下图所示:

01

Tagged Pointer

为了改进上面提到的内存占用和效率问题,苹果提出了Tagged Pointer对象。由于 NSNumberNSDate 一类的变量本身的值需要占用的内存大小常常不需要8个字节,拿整数来说,4 个字节所能表示的有符号整数就可以达到 20 多亿(注:2^31=2147483648,另外 1 位作为符号位),对于绝大多数情况都是可以处理的。

所以我们可以将一个对象的指针拆成两部分,一部分直接保存数据,另一部分作为特殊标记,表示这是一个特别的指针,不指向任何一个地址。所以,引入了Tagged Pointer对象之后,64CPUNSNumber 的内存图变成了以下这样:

02
  • Tagged Pointer是专⻔⽤来存储⼩的对象,例如NSNumber,NSDate等。
  • Tagged Pointer指针的值不再是地址了,⽽是真正的值。所以,实际上它不再是⼀个对象了,它只是⼀个披着对象⽪的普通变量⽽已。所以,它的内存并不存储在堆中,也不需要mallocfree
  • 当指针不够存储数据时,就会使用动态分配内存的方式来存储数据。
  • 在内存读取上有着3倍的效率,创建时⽐以前快106倍。

总结

苹果将Tagged Pointer引入,给64位系统带来了内存的节省和运行效率的提高。Tagged Pointer通过在其最后一个 bit 位设置一个特殊标记,用于将数据直接保存在指针本身中。因为Tagged Pointer并不是真正的对象。

nonpointer

isa

nonpointer

  • 0,代表普通的指针,存储着ClassMeta-Class对象的内存地址
  • 1,代表优化过,使用位域存储更多的信息

has_assoc

  • 是否有设置过关联对象,如果没有,释放时会更快

has_cxx_dtor

  • 是否有C++的析构函数(.cxx_destruct),如果没有,释放时会更快

shiftcls

  • 存储着ClassMeta-Class对象的内存地址信息

magic

  • 用于在调试时分辨对象是否未完成初始化

weakly_referenced

  • 是否有被弱引用指向过,如果没有,释放时会更快

deallocating

  • 对象是否正在释放

extra_rc

  • 里面存储的值是引用计数器减1

has_sidetable_rc

  • 引用计数器是否过大无法存储在isa
  • 如果为1,那么引用计数会存储在一个叫SideTable的类的属性中

参考链接:巧神:
深入理解Tagged Pointer

你可能感兴趣的:(Tagged Pointer 和 nonpointer)