【iOS】Tagged Pointer

23 篇文章 0 订阅
18 篇文章 0 订阅

Tagged Pointer

内存布局

在这里插入图片描述

  • 代码段:编译之后的代码
  • 数据段
    • 字符串常量:比如NSString *str = @“123”
    • 已初始化数据:已初始化的全局变量、静态变量等
    • 未初始化数据:未初始化的全局变量、静态变量等
  • 堆:通过alloc、malloc、calloc等动态分配的空间,分配的内存空间地址越来越大-
  • 栈:函数调用开销,比如局部变量。分配的内存空间地址越来越小

Tagged Pointer

  • 从64bit开始,iOS引入了Tagged Pointer技术,用于优化NSNumber、NSDate、NSString等小对象的存储
  • 在没有使用Tagged Pointer之前, NSNumber等对象需要动态分配内存、维护引用计数等,NSNumber指针存储的是堆中NSNumber对象的地址值
  • 使用Tagged Pointer之后,NSNumber指针里面存储的数据变成了:Tag + Data,也就是将数据直接存储在了指针中
  • 当指针不够存储数据时,才会使用动态分配内存的方式来存储数据
  • objc_msgSend能识别Tagged Pointer比如NSNumber的intValue方法,直接从指针提取数据,节省了以前的调用开销

在小对象的时候, 使用Tagged Pointer,NSNumber指针里面存储的数据变成了:Tag + Data,也就是将数据直接存储在了指针中,不在需要存取来获取值了。

我们分别打印abcdefghijk和abc字符串的类型。

NSString *str1 = [NSString stringWithFormat:@“abc”];
NSString *str2 = [NSString stringWithFormat:@“abcdefghijk”];
NSLog(@“\n[str1 class]=%@\n[str2 class]=%@”,[str1 class],[str2 class]);

打印结果:
在这里插入图片描述

根据打印发现str1是NSTaggedPointerString类型,是不通过set方法找对象的。

我们也可以在源码中找到相关实现,

    1. 在NSObject.mm中查找retain方法的实现
- (id)retain {
	return ((id)self)->rootRetain();
}
    1. 点击进入rootRetain方法,我们可以在里面找到:
      if (isTaggedPointer())
      return (id)this;
      也就是说如果是TaggedPointer类型,直接返回,不需要根据指针查找。

判断是否是TaggedPointer

我们点击isTaggedPointer方法

_objc_isTaggedPointer(const void * _Nullable ptr)
{
return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
}

  • define _OBJC_TAG_MASK 1UL
#if TARGET_OS_OSX && __x86_64__
#   define OBJC_MSB_TAGGED_POINTERS 0
#else
#   define OBJC_MSB_TAGGED_POINTERS 1
#endif


#if OBJC_MSB_TAGGED_POINTERS
#   define _OBJC_TAG_MASK (1UL<<63)
#else
#   define _OBJC_TAG_MASK 1UL
#endif

在判断是否是TaggedPointer的时候,在iOS平台和MAC平台还是不太一样的

1、iOS平台,需要把1向左移动63位,也就是最高有效位是1(第64bit)
2、在Mac平台,最低有效位是1

面试题

  1. 什么是Tagged Pointer?
    答:Tagged Pointer是一种优化技术,用于存储小对象(如NSNumber、NSDate和NSString)的值。与普通对象不同,Tagged Pointer直接将数据存储在指针本身,而不是存储在堆上的内存块。这种技术利用了64位指针的高位来存储类型信息和实际数据,从而避免了分配和释放内存的开销,提高了性能和效率。

  2. Tagged Pointer的工作原理是什么?
    答:
    Tagged Pointer的工作原理基于将指针的部分位 用作标记(tag)和数据存储。具体实现如下:

    1. 标记位(Tag Bits):在64位指针的高位部分,用若干位来存储标记信息。这些标记位用来区分不同类型的Tagged Pointer(如NSNumber、NSDate等)。
    2. 数据位(Data Bits):在剩余的位中直接存储实际数据。例如,对于NSNumber,可以直接存储整数值或浮点数值。
    3. 类型检测:在需要时,通过检测指针的高位标记,判断该指针是否是Tagged Pointer以及它的类型。
  3. 如果检测一个指针是否是Tagged Pointer?
    答:
    检测一个指针是否是Tagged Pointer,可以通过检查指针的高位标记。具体实现方式因不同版本的Objective-C运行时而异,但常见的方式是检查指针是否设置了某些特定位。例如,在64位系统上,如果指针的最高位为1,则该指针可能是Tagged Pointer。代码示例如下:

BOOL isTaggedPointer(id pointer) {
return ((uintptr_t)pointer & _OBJC_TAG_MASK) != 0;
}
其中,_OBJC_TAG_MASK是用于检测Tagged Pointer的掩码。

  1. 给出一个Tagged Pointer NSNumber的示例,并解释其内部结构

假设我们有一个Tagged Pointer类型的NSNumber对象,其内部结构可能如下:

  • 标记位:在指针的高位,用于标记该指针是Tagged Pointer以及它的类型(例如,NSNumber)。
  • 数据位:在指针的低位,存储实际的数值数据。

例如,假设一个64位指针的结构如下:

[63 ... 60] [59 ... 4] [3 ... 0]
  标记位       数据位    类型标记

假设标记位为0b1110(表示NSNumber),类型标记为0b0001(表示整数类型),数据位为0b0000000000000000000000000000000000000000000000000000000000001101(表示整数13)。则这个Tagged Pointer的值表示一个NSNumber对象,其数值为13。

怎么理解这个?
这个比特位分布,和之前介绍的 isa_t 结构有些不同。这个新的比特位分布是 Objective-C 运行时系统中另一种优化技术 - “Packed Tagged Pointer”。

下面我来解释一下这个结构的每个部分:

[63 … 60] 标记位 (4 bits):
这 4 个比特位用于标识这是一个 Packed Tagged Pointer,而不是普通的 isa 指针。
它们会被设置为一个特殊的标记值,用于在运行时快速识别这种指针。
[59 … 4] 数据位 (56 bits):
这 56 个比特位用于存储对象的数值信息和类信息。
具体的编码方式取决于对象的类型。
[3 … 0] 类型标记 (4 bits):
这 4 个比特位用于标识对象的具体类型,比如整数、浮点数、字符串等。

运行时系统会根据这个类型标记来解析存储在数据位中的实际值。
与之前介绍的 Tagged Pointer 相比,这种 Packed Tagged Pointer 的优势是能够存储更多的数据信息,提高了存储密度。

  • 26
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值