【iOS】—— Tagged Pointer

【iOS】—— Tagged Pointer

    • 关于Tagged Pointer
      • Tagged Pointer的介绍
      • NSTaggedPointer示例
      • NSTaggedPointer结构
      • Tagged Pointer的特点
      • 注意事项
        • isa指针
        • isa指针的优化

关于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示例

 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内部实现了编码、解码方法,我们看一下:
编码:

 static inline void * _Nonnull
_objc_encodeTaggedPointer(uintptr_t ptr)
{uintptr_t value = (objc_debug_taggedpointer_obfuscator ^ ptr);
#if OBJC_SPLIT_TAGGED_POINTERSif ((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;
#endifreturn (void *)value;
}

我们可以试着打印地址:

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的值写回。

接下来看一道题

 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"];});}
 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 指针的值不再是地址了,而是真正的值。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/3246073.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

推荐一款处理TCP数据的架构--EasyTcp4Net

EasyTcp4Net是一个基于c# Pipe,ReadonlySequence的高性能Tcp通信库,旨在提供稳定,高效,可靠的tcp通讯服务。 基础的消息通讯 重试机制 超时机制 SSL加密通信支持 KeepAlive 流量背压控制 粘包和断包处理 (支持固定头处理,固定长度处理,固定字符处理) 日志支持Pipe &…

智能守护校园餐桌:校园阳光食堂视频AI监控智能管理方案

一、背景分析 随着科技的飞速发展&#xff0c;智能化、信息化已成为现代校园管理的重要趋势。校园食堂作为学校重要的服务设施&#xff0c;其食品安全、环境卫生和秩序管理显得尤为重要。作为校园生活中不可或缺的一部分&#xff0c;食堂的管理也急需引入先进技术&#xff0c;…

LNMP架构部署及应用

部署LNMP架构流程 1.安装Nginx&#xff08;上传软件包&#xff0c;执行脚本&#xff09; yum -y install pcre-devel zlib-devel gcc gcc useradd -M -s /sbin/nologin nginx tar zxf nginx-1.12.0.tar.gz cd nginx-1.12.0 ./configure --prefix/usr/local/nginx --usernginx…

字符串类中的常用方法

1 string对象的创建 静态创建 String s1  "abc";  String s2  "abc";  动态创建 String s3  new String("abc"); String s4  new String("abc"); 2string对象的不可变性 任何一个String对象在创建之后都不能对它的…

等级保护测评(三级)主机linux测评指导书

等级保护测评指导书分技术&#xff08;物理安全、主机安全、网络安全、应用安全、数据安全&#xff09; 和管理&#xff08;安全管理制度、安全管理机构、人员安全管理、系统建设管理、系统运维管理&#xff09;两大块。 今天给大家分享一下&#xff0c;等级保护测评&#xff0…

如何在电脑上演示手机上APP,远程排查移动端app问题

0序&#xff1a; 对接客户&#xff0c;给领导演示移动端产品&#xff0c;或者远程帮用户排查移动端产品的问题。都需要让别人能够看到自己在操作手机。 会议室可以使用投屏&#xff0c;但需要切换电脑和手机。 排查问题经常都是截图、或者手机上录制视频&#xff0c;十分繁琐…

NGFW和防火墙的区别?

NGFW&#xff08;Next Generation Firewall&#xff0c;下一代防火墙&#xff09;和FW&#xff08;Firewall&#xff0c;防火墙&#xff09;在网络安全领域都扮演着重要角色&#xff0c;但它们在功能、性能和应用场景上存在显著的区别。以下是NGFW和FW之间的主要区别&#xff1…

零基础STM32单片机编程入门(十五) DHT11温湿度传感器模块实战含源码

文章目录 一.概要二.DHT11主要性能参数三.DHT11温度传感器内部框图四.DTH11模块原理图五.DHT11模块跟单片机板子接线和通讯时序1.单片机跟DHT11模块连接示意图2.单片机跟DHT11模块通讯流程与时序 六.STM32单片机DHT11温度传感器实验七.CubeMX工程源代码下载八.小结 一.概要 DH…

ollama + fastgpt 搭建免费本地知识库

目录 1、ollama ollama的一些操作命令: 使用的方式: 2、fastgpt 快速部署: 修改配置: config.json: docker-compose.yml: 运行fastgpt: 访问OneApi: 添加令牌和渠道: 登陆fastgpt,创建知识库和应用 3、总结: 附录: 1. 11434是ollama的端口: 2. m3e 测…

Docker-Nvidia(NVIDIA Container Toolkit)

安装NVIDIA Container Toolkit工具&#xff0c;支持docker使用GPU 目录 1.NVIDIA Container Toolkit 安装1.1 nvidia-docker安装1.2 验证1.2.1 验证安装1.2.2 额外补充 1.NVIDIA Container Toolkit 安装 1.1 nvidia-docker安装 NVIDIA/nvidia-docker Installing the NVIDIA …

Mongodb多键索引中索引边界的混合

学习mongodb&#xff0c;体会mongodb的每一个使用细节&#xff0c;欢迎阅读威赞的文章。这是威赞发布的第93篇mongodb技术文章&#xff0c;欢迎浏览本专栏威赞发布的其他文章。如果您认为我的文章对您有帮助或者解决您的问题&#xff0c;欢迎在文章下面点个赞&#xff0c;或者关…

[Python库](3) Arrow库

目录 1.简介 2.安装 3.函数 3.1.获取当前UTC时间( 世界协调时时间 ) 3.2.格式化日期 3.3.创建Arrow对象 3.4.时间改变 3.5.获取时间戳 3.6.时区改变 4.小结 1.简介 Arrow库是一个Python库&#xff0c;提供了一套用于处理日期和时间的API。Arrow库特别适合在需要进行大…

英福康INFICON TranspectorWare v3 RGA软件操作说明

英福康INFICON TranspectorWare v3 RGA软件操作说明

uniapp开发钉钉小程序流程

下载开发工具 1、小程序开发工具 登录钉钉开发平台&#xff0c;根据自己的需求下载合适的版本&#xff0c;我这里下载的是Windows &#xff08;64位&#xff09;版本 小程序开发工具 - 钉钉开放平台 2、HBuilder X HBuilderX-高效极客技巧 新建项目及相关配置 新建项目 …

Matlab|基于蒙特卡洛法的电动汽车充电负荷计算

目录 1 主要内容 2 部分代码 3 程序结果 4 下载链接 1 主要内容 该程序方法复现《V2G 模式下含分布式能源的配电网优化运行研究》第二章电动汽车无序充电模型&#xff0c;按照文章《V2G 模式下基于复杂网络的电动汽车有序充电策略》分析思路研究了不同数量电动汽车接入情况…

图示 JVM 可达性分析算法

可达性分析算法&#xff1a; 以 GC Roots 为起始点进行搜索&#xff0c;可达的对象都是存活的&#xff0c;不可达的对象可被回收。 Java 虚拟机使用该算法来判断对象是否可被回收&#xff0c;GC Roots 一般包含以下内容&#xff1a; 虚拟机栈中局部变量表中引用的对象本地方法栈…

谷歌账号异常的常见8种状态,这两种通常自己多尝试就有希望

这是系列文章的第二篇&#xff0c;GG账号服务为您介绍账号谷歌账号异常的8种情况&#xff08;状态&#xff09;。 昨天我们介绍了三种直接进入手机号码验证&#xff0c;且可以输入手机号码的情形&#xff0c;也分析了原因&#xff0c;以及解决的方法和注意要点。 今天我们来介…

分享3个好用的启动盘u盘制作工具

对于经常需要安装维护电脑的同学&#xff0c;制作一个可启动的U盘是非常有必要的。小编今天就和大家分享三款优秀的U盘启动盘制作工具&#xff1a;Ventoy、UltraISO和Rufus。 1. Ventoy Ventoy是一款开源的启动U盘制作工具&#xff0c;它支持将ISO、WIM、IMG、VHD(x)和EFI等类…

19.x86游戏实战-创建MFC动态链接库

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 工具下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1rEEJnt85npn7N38Ai0_F2Q?pwd6tw3 提…

6.Dockerfile及Dockerfile常用指令

Dockerfile是构建docker镜像的脚本文件 Dockerfile有很多的指令构成&#xff0c;指令由上到下依次运行。 每一条指令就是一层镜像&#xff0c;层越多&#xff0c;体积就越大&#xff0c;启动速度也越慢 井号开头的行是注释行。指令写大写写小写都行&#xff0c;但一般都写为…