• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Objective-C Tagged Pointer

武飞扬头像
lincy
帮助1

Tagged Pointer

在计算机科学领域,一个指针或地址空间,除了存放对象地址之外,也可以存放其他额外的信息,并将其中的一些bit位作为tag标记区分,这个就叫tagged pointer。

具体到Objective-C,一个指针(地址)需要用64位也就是16-byte的内存空间来存储,但实际上往往用不到全部,只用到了中间的部分,高位16-bit用不到,由于内存按字节对齐的要求,所以低位值也总是为0:

学新通网

正常情况下,内存中对象和指针的关系是:一个指针的值存放的是一个地址,地址表示另一块内存区域,对象本身存放在那块内存区域中,这边需要的内存空间=对象占用的空间 指针占用的内存空间。

那么如果一个对象本身所需要的空间很小,少于一个指针所需要的空间,这样存放不就造成了不必要的内存浪费了吗,因此,可以将一些内存占用不大的对象(比如NSNumber、NSString、NSDate等)直接存到这个指针的内存空间中,这样就不需要再额外开辟一个空间来存放它,需要的内存空间=对象占用的空间,显著的节省了内存空间。

为了区分这个pointer是只用于存放地址的普通的pointer还是存放了其他信息的pointer,就选取其中的某个或某些位作为标记tag,剩下的位就可以用于存放所需要的数据,叫做payload,这样的指针就是tagged pointer。Objective-C runtime中,isa指针就是使用了tagged pointer来优化了内存。

在”objc-object.h”中,提供了内部接口判断是否是tagged pointer

inline bool
objc_object::isTaggedPointer()

Tagged Pointer的Tag和Payload

一个tagged pointer中,有一位作为mask用来标识是否是tagged pointer,接着是3位存放tag信息,tag可以取值0-7,如果值是0-6,表示它是一个basic tagged pointer, 而7作为保留值,表示是它是extended tagged pointer

basic tagged pointer中除了1位用于标识是否是tagged pointer,接下来3位存放对象类型的索引值,剩余60位是payload,用于存放想要存放的数据,比如对象的值。

extended tagged pointer中有1位mask和3位tag都取值1,用于表示是extended tagged pointer,接下来8位存放类型索引值,剩余52位作为payload存放数据。这样可以存放多达256种类型,代价是payload从60位减少到52位,能存放的数据减少了。

“objc-internal.h”中定义了tagged pointer对象类型的枚举:

enum objc_tag_index_t : uint16_t
{
    // 60-bit payloads
    OBJC_TAG_NSAtom            = 0,
    OBJC_TAG_1                 = 1,
    OBJC_TAG_NSString          = 2,
    OBJC_TAG_NSNumber          = 3,
    OBJC_TAG_NSIndexPath       = 4,
    OBJC_TAG_NSManagedObjectID = 5,
    OBJC_TAG_NSDate            = 6,

    // 60-bit reserved
    OBJC_TAG_RESERVED_7        = 7,
    
    // 52-bit payloads
    OBJC_TAG_Photos_1          = 8,
    OBJC_TAG_Photos_2          = 9,
    OBJC_TAG_Photos_3          = 10,
    OBJC_TAG_Photos_4          = 11,
    OBJC_TAG_XPC_1             = 12,
    OBJC_TAG_XPC_2             = 13,
    OBJC_TAG_XPC_3             = 14,
    OBJC_TAG_XPC_4             = 15,

    OBJC_TAG_First60BitPayload = 0,
    OBJC_TAG_Last60BitPayload  = 6,
    OBJC_TAG_First52BitPayload = 8,
    OBJC_TAG_Last52BitPayload  = 263,

    OBJC_TAG_RESERVED_264      = 264
};

具体哪些位存放tag和类型索引?

x86_64架构中,是基于LSB最低有效位,存放在低位: 学新通网 学新通网

Arm架构中,是基于MSB最高有效位,存放在高位: 学新通网 学新通网 为什么ARM上是高位表示,这边涉及到了msgSend的一项优化,放在高位可以使msgSend节省一个判断分支。

但是,iOS14后(WWDC2020中介绍),ARM平台上tag信息存放位做了一些调整,优化了读取二进制文件中的常量数据的内存消耗。具体来说,将3位tag放到了低位,同样的如果tag位位7,也是extended tagged pointer,扩展信息放在紧接着mask位后8位: 学新通网

One more thing...

runtime中tagged pointer是被混淆过的,直接读取pointer内部的位得不到正确的信息的,tagged pointer内部实现的具体代码可以查看”objc-internal.h”

// 用于混淆tagged pointer的因子,会被初始化为一个随机数
extern uintptr_t objc_debug_taggedpointer_obfuscator;   

static inline void * _Nonnull
_objc_encodeTaggedPointer(uintptr_t ptr);

static inline uintptr_t
_objc_decodeTaggedPointer(const void * _Nullable ptr);

static inline bool
_objc_taggedPointersEnabled(void);

static inline void * _Nonnull
_objc_makeTaggedPointer(objc_tag_index_t tag, uintptr_t value);

static inline bool
_objc_isTaggedPointer(const void * _Nullable ptr);

static inline objc_tag_index_t
_objc_getTaggedPointerTag(const void * _Nullable ptr);

static inline uintptr_t
_objc_getTaggedPointerValue(const void * _Nullable ptr);

static inline intptr_t
_objc_getTaggedPointerSignedValue(const void * _Nullable ptr);

总结:

  • Tagged pointer不是一个正常的指针,是对指针内存空间的最大化利用,使用其中的一些bit位来和普通指针进行区分,剩余的位存放数据
  • runtime中利用tagged pointer技术存放一些占用较小的对象,显著的优化了内存
  • runtime中对tagged pointer做了混淆,提升了安全性

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhbafifg
系列文章
更多 icon
同类精品
更多 icon
继续加载