【iOS】—— Tagged Pointer

【iOS】------ Tagged Pointer

关于Tagged Pointer

为了节省内存和提高执行效率,苹果提出了Tagged Pointer的概念。先看看原有的对象为什么会浪费内存。假设要存储一个NSNumber对象,其值是一个整数。正常情况下,如果这个整数只是一个NSInteger的普通变量,那么它所占用的内存是与CPU的位数有关,在32位CPU下占4个字节,在64位CPU下是占8个字节的。而指针类型的大小通常也是与CPU位数相关,一个指针所占用的内存在32位CPU下为4个字节,在64位CPU下也是8个字节。所以一个普通的iOS程序,如果没有Tagged Pointer对象,从32位机器迁移到64位机器中后,虽然逻辑没有任何变化,但这种NSNumber、NSDate一类的对象所占用的内存会翻倍。

Tagged Pointer的介绍

为了存储和访问一个NSNumber对象,我们需要在堆上为其分配内存,另外还要维护它的引用计数,管理它的生命期。这些都给程序增加了额外的逻辑,造成运行效率上的损失。

对此提出了Tagged Pointer概念,由于NSNumber、NSDate一类的变量本身的值需要占用的内存大小常常不需要8个字节,拿整数来说,4个字节所能表示的有符号整数就可以达到20多亿。所以我们可以将一个对象的指针拆成两部分,一部分直接保存数据,另一部分作为特殊标记,表示这是一个特别的指针,不指向任何一个地址。

简单来理解就是把指针指向的内容,直接放到了指针变量的内存地址中。 于是使用了标签指针这种方式来优化数据的存储方式。在运行时根据实际情况创建。

NSTaggedPointer示例

objective-c 复制代码
 NSString *string = nil;
 NSMutableString *mutableString = [NSMutableString stringWithFormat:@"abcde"];
 for (int i = 0; i < 13; i++) {
     [mutableString appendString:@"c"];
     string = [mutableString copy];
     NSLog(@"%@ %p %@", string, string, [string class]);
}

输出结果:


当字符长度在10以内的时候,字符串的类型都是NSTaggedPointer类型,当超过10时,就变成了__NSCFString。

NSTaggedPointer结构

苹果为了安全对其做了编码,runtime内部实现了编码、解码方法,我们看一下:

编码:

objective-c 复制代码
 static inline void * _Nonnull
_objc_encodeTaggedPointer(uintptr_t ptr)
{
    uintptr_t value = (objc_debug_taggedpointer_obfuscator ^ ptr);
#if OBJC_SPLIT_TAGGED_POINTERS
    if ((value & _OBJC_TAG_NO_OBFUSCATION_MASK) == _OBJC_TAG_NO_OBFUSCATION_MASK)
        return (void *)ptr;
    uintptr_t basicTag = (value >> _OBJC_TAG_INDEX_SHIFT) & _OBJC_TAG_INDEX_MASK;
    uintptr_t permutedTag = _objc_basicTagToObfuscatedTag(basicTag);
    value &= ~(_OBJC_TAG_INDEX_MASK << _OBJC_TAG_INDEX_SHIFT);
    value |= permutedTag << _OBJC_TAG_INDEX_SHIFT;
#endif
    return (void *)value;
}

我们可以试着打印地址:

objective-c 复制代码
NSNumber *number1 = [NSNumber numberWithInt:1];
NSLog(@"number1 pointer is %p", number1);

输出结果:

  • Tagged Pointer 标记:x86最后一位是标记位,arm64最高位是标记位。1表示是Tagged Pointer对象,0表示是普通对象。
  • Tag:对象类型标记。x86为13位,arm64为02。7表示有扩展信息。
  • Extended:x86为411位,arm64为5462。用来扩展更多类型。
  • payload:有效负载。存储真正的数据(除了标记位、tag以及extended),不过为了安全苹果做了编码。

Tagged Pointer的特点

  1. Tagged Pointer专门用来存储小的对象,比如NSNumber,NSDate。
  2. Tagged Pointer指针指向的不再是内存地址,而是一个真正的值。所以也不是一个对象,而是披着对象外衣的变量。内存也不存储在堆上,不需要使用malloc和free。
  3. 减少了 64位机器下程序的内存占用,还提高了运行效率,完美地解决了小内存对象在存储和访问效率上的问题。

注意事项

isa指针

Tagged Pointer 的引入也带来了问题,即 Tagged Pointer 并不是真正的对象,而是一个伪对象,所以你如果完全把它当成对象来使用,可能会让它"露马脚"。在上一章中我们写道,所有对象都有isa 指针,而 Tagged Pointer 其实是没有的,因为它不是真正的对象。

isa指针的优化

除了引入Tagged Pointer来优化小的对象,也普通对象的isa指针进行了优化和调整。

在32 位环境下,对象的引用计数都保存在一个外部的表中,每一个对象的 Retain 操作,实际上包括如下 5个步骤:

  1. 获取全局记录引用计数的哈希表。
  2. 为了线程安全,给哈希表上锁。
  3. 找到目标对象的引用计数。
  4. 将引用计数+1,写回哈希表。
  5. 给该哈希表解锁。

为了保证线程安全,对引用计数的增减操作都要先锁定这个表,这从性能上看是非常差的。

在64位的情况下,指针也是64位,实际作为指针部分的只有33位,剩下的31位中,19位用于保存引用计数,当引用计数超过了19位时,才会保存到外部表中,这样引用计数的更改效率提高。

与前面的5个步骤对应,在64位环境下,新的 Retain 操作包括如下 5个步骤:

  1. 检查isa指针是有存在标记位,如果不存在,就执行以前的方法。负责执行的二步。
  2. 判断当前的对象是否正在释放,如果是,就不用进行操作。
  3. 增加对象的引用计数,先不写回isa指针中。
  4. 判断引用计数的位数是否可以被19位表示,如果不能就执行原来的方法,否则执行下一步。
  5. 进行原子的写操作,将isa的值写回。

接下来看一道题

objective-c 复制代码
 dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
 for (int i = 0; i < 1000; i++) {
     dispatch_async(queue, ^{
        p.name = [NSString stringWithFormat:@"addafghsdddds"];
     });
 }
objective-c 复制代码
 dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
 for (int i = 0; i < 1000; i++) {
     dispatch_async(queue, ^{
         p.name = [NSString stringWithFormat:@"ad"];
     });
 }

两段代码唯一的区别就是一个name属性所赋值的字符串长一些长度大于10,另一个长度小一点小于10。我们去运行它,就会发现,第一段代码程序崩溃,第二段没有崩溃。

原因就是:
第一段代码并发访问了共享数据 p.name。在多线程环境下,同时对同一变量进行写操作可能引发竞争条件或数据不一致的问题。要解决它就要给它加上锁。
而第二段因为字符串短,所以被改为了Tagged Pointer对象:Tagged Pointer 指针的值不再是地址了,而是真正的值。

相关推荐
幽夜落雨5 分钟前
ios老版本应用安装方法
ios
gxhlh6 小时前
局域网中 Windows 与 Mac 互相远程连接的最佳方案
windows·macos
宏基骑士6 小时前
mac 电脑上安装adb命令
macos·adb
胖虎18 小时前
实现 iOS 自定义高斯模糊文字效果的 UILabel(文末有Demo)
ios·高斯模糊文字·模糊文字
水银嘻嘻13 小时前
【Mac】Python相关知识经验
开发语言·python·macos
梦魇梦狸º1 天前
mac 配置 python 环境变量
chrome·python·macos
丁总学Java1 天前
macOS如何进入 Application Support 目录(cd: string not in pwd: Application)
macos
qdprobot1 天前
Mixly米思齐1.0 2.0 3.0 软件windows版本MAC苹果电脑系统安装使用常见问题与解决
windows·macos
麦克Mapp1 天前
不用安装双系统,如何在mac上玩windows游戏呢?
macos
符小易1 天前
Mac苹果电脑 怎么用word文档和Excel表格?
macos·word·excel