【iOS】——TaggedPointer

TaggedPointer介绍

在为了改进从 32位CPU 迁移到 64位CPU内存浪费和效率问题,在 64位CPU 环境下,引入了 Tagged Pointer 。旨在提高内存效率和运行性能,尤其针对小的、频繁使用的对象,如NSNumber, NSDate, 和NSString等。在64位处理器上,每个指针占用8字节,而某些对象可能只包含少量数据,因此使用完整对象和指针来管理这些小对象可能造成不必要的内存消耗和性能开销。

内存分布

NSInteger 封装成 NSNumber 为例,内存分布图如下:

未引入TaggedPointer时

未引入TaggedPointer内存分布图

在32位处理器中,对象占用的内存有12字节

在64位处理器中,对象占用的内存有24字节,可见翻了一倍

引入TaggedPointer时

引入TaggedPointer内存分布图

在32位处理器中,对象占用的内存有12字节

在64位处理器中,对象占用的内存只有8字节,节省了4个字节的空间,而且引用计数 retainCount最大值

引用了 Tagged Pointer 的对象,节省了分配在堆区的空间,将值存在指针区域的栈区。从而节省了内存空间以及大大提升了访问速度。

TaggedPointer特点

在WWDC2013中苹果介绍了Tagged Pointer有以下特点:

  • Tagged Pointer专门用来存储小的对象,例如NSNumberNSDate

  • Tagged Pointer指针的值不再是地址了,而是真正的值。所以,实际上它不再是一个对象了,它只是一个披着对象皮的普通变量而已。所以,它的内存并不存储在堆中,也不需要 malloc 和 free。

  • 在内存读取上有着 3 倍的效率,创建时比以前快 106 倍。

在OC中每个对象都有isa指针,但Tagged Pointer没有,因为Tagged Pointer并不是真正的对象,它没有在堆上分配空间而是直接将值存在指针区域的栈区。如果直接访问Tagged Pointer`

类型的变量的isa指针的话就会在编译时发出警告:

img

只要避免在代码中直接访问对象的 isa 变量,即可避免这个问题。可以使用isKindOfClassobject_getClass

TaggedPointer数据混淆

运行下面代码:

int main(int argc, const char * argv[]) {@autoreleasepool {NSString *str = [NSString stringWithFormat:@"L"];NSLog(@"%p - %@ - %@",str,str,str.class);}return 0;
}

在这里插入图片描述

通过数据类型可以看出是TaggedPointer,但是它的地址看着十分奇怪这是因为它做了数据混淆。

数据混淆可以防止恶意用户或逆向工程师直接从内存中读取并理解数据,特别是当这些数据包含敏感信息时。

通过查看源码可知异或一个objc_debug_taggedpointer_obfuscator

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;
}

接着搜索可知这是一个随机值在iOS12之后引入的

static void
initializeTaggedPointerObfuscator(void)
{if (!DisableTaggedPointerObfuscation && dyld_program_sdk_at_least(dyld_fall_2018_os_versions)) {//此处是随机值   arc4random_buf(&objc_debug_taggedpointer_obfuscator,sizeof(objc_debug_taggedpointer_obfuscator));objc_debug_taggedpointer_obfuscator &= ~_OBJC_TAG_MASK;
#if OBJC_SPLIT_TAGGED_POINTERSobjc_debug_taggedpointer_obfuscator &= ~(_OBJC_TAG_EXT_MASK | _OBJC_TAG_NO_OBFUSCATION_MASK);int max = 7;for (int i = max - 1; i >= 0; i--) {int target = arc4random_uniform(i + 1);swap(objc_debug_tag60_permutations[i],objc_debug_tag60_permutations[target]);}
#endif} else {objc_debug_taggedpointer_obfuscator = 0;}
}

是否开启混淆通过DisableTaggedPointerObfuscation来控制,如果开启了混淆会给objc_debug_taggedpointer_obfuscator赋值一个随机数。如果没有开启混淆会将objc_debug_taggedpointer_obfuscator赋值为0

解决TaggedPointer数据混淆

这里提供两种方法解决数据混淆,第一种是通过解密函数,第二种是改变环境配置

解密函数

通过上面的源代码不难发现是原来的数据异或一个objc_debug_taggedpointer_obfuscator,如果再异或一次就能得到原来的数据

extern uintptr_t objc_debug_taggedpointer_obfuscator;uintptr_t
ssl_objc_decodeTaggedPointer(id ptr)
{// 再次异或解密return (uintptr_t)ptr ^ objc_debug_taggedpointer_obfuscator;
}int main(int argc, const char * argv[]) {@autoreleasepool {NSString *str = [NSString stringWithFormat:@"L"];NSLog(@"%p - %@ - %@ -0x%lx",str,str,str.class,ssl_objc_decodeTaggedPointer(str));}return 0;
}

在这里插入图片描述

0x800000000000260b就是我们想要的数据

改变环境变量

  1. 打开Xcode项目。
  2. 在顶部菜单栏中选择“Product”,然后选择“Scheme”,再点击“Edit Scheme…”。或者也可以在右侧的“Scheme”面板中点击齿轮图标选择“Edit Scheme…”。
  3. 在弹出的编辑Scheme窗口中,左侧选择你的目标App或Test Bundle。
  4. 在“Run”或“Profile”或“Analyze”或“Test”的标签页中找到“Environment Variables”。
  5. 在“Environment Variables”列表中,设置环境变量OBJC_DISABLE_TAG_OBFUSCATIONYES,关闭数据混淆

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

x86-64下的TaggedPointer结构

TaggedPointer标志位

下面的源码是判断是否为tagged pointer类型:

static inline bool 
_objc_isTaggedPointer(const void * _Nullable ptr)
{return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
}
#endif

这里用到了_OBJC_TAG_MASK掩码,来看下它的定义:

#if __arm64__
#   define OBJC_SPLIT_TAGGED_POINTERS 1
#else
#   define OBJC_SPLIT_TAGGED_POINTERS 0
#endif#if OBJC_SPLIT_TAGGED_POINTERS   // arm64时为 1
#   define _OBJC_TAG_MASK (1UL<<63)
#else 
#   define _OBJC_TAG_MASK 1UL

x86-64环境下_OBJC_TAG_MASK的值为1ptr & 1还是等于1,也就是说指针地址最低位是1的时候,代表这个指针是tagged pointer类型。

类标志位

下面是类的标志位

    // 60-bit payloadsOBJC_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,

1-3位是类标志位

数据类型和所占位数

第4-7位表示数据类型或字符串长度

在这里插入图片描述

数据存储和结构图

tagged pointer8-63位用来存储数据

在这里插入图片描述

arm64下的TaggedPointer结构

int main(int argc, const char * argv[]) {@autoreleasepool {NSString *str = [NSString stringWithFormat:@"a"];NSNumber *num1 = @3;NSNumber *num2 = @(0xFFFFFFFFFFFFFFFF);NSLog(@"%p- %p - %p",str,num1,num2);}return 0;}

在这里插入图片描述

TaggedPointer标志位

#if OBJC_SPLIT_TAGGED_POINTERS
#   define _OBJC_TAG_MASK (1UL<<63)
#   define _OBJC_TAG_INDEX_SHIFT 0
#   define _OBJC_TAG_SLOT_SHIFT 0
#   define _OBJC_TAG_PAYLOAD_LSHIFT 1
#   define _OBJC_TAG_PAYLOAD_RSHIFT 4
#   define _OBJC_TAG_EXT_MASK (_OBJC_TAG_MASK | 0x7UL)
#   define _OBJC_TAG_NO_OBFUSCATION_MASK ((1UL<<62) | _OBJC_TAG_EXT_MASK)
#   define _OBJC_TAG_CONSTANT_POINTER_MASK \~(_OBJC_TAG_EXT_MASK | ((uintptr_t)_OBJC_TAG_EXT_SLOT_MASK << _OBJC_TAG_EXT_SLOT_SHIFT))
#   define _OBJC_TAG_EXT_INDEX_SHIFT 55
#   define _OBJC_TAG_EXT_SLOT_SHIFT 55
#   define _OBJC_TAG_EXT_PAYLOAD_LSHIFT 9
#   define _OBJC_TAG_EXT_PAYLOAD_RSHIFT 12

arm64环境下_OBJC_TAG_MASK的值为 (1UL<<63)ptr & (1UL<<63)还是等于(1UL<<63),也就是说指针地址第63位是1的时候,代表这个指针是tagged pointer类型。

类标志位

类标志位放在了地址的前三位,即0-2

数据类型&字符串长度

数据类型&字符串长度放在地址的3-6

数据存储和结构图

762位用来存储数据

在这里插入图片描述

TaggedPointer结构总结

在x86结构中:

  • 地址最低位也就是0位是TaggedPointer标志位
  • 第1-3位是类标志位
  • 第4-7位表示数据类型或字符串长度
  • 8-63位用来存储数据

在arm64架构中:

  • 地址最高位也就是63位是TaggedPointer标志位
  • 类标志位放在了地址的前三位,即0-2
  • 数据类型&字符串长度放在地址的3-6
  • 762位用来存储数据

Tagged Pointer可表示的数字范围是-2^55+1 ~ 2^55-1,超出这个范围的数字,NSNumber会转换为普通的Objective-C对象分配在堆上。

Tagged Pointer可表示的字符串范围是9个字符。字符串会转成__NSCFString类型

TaggedPointer面试题

执行下面两段代码,有什么区别?

dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (int i = 0; i < 1000; i++) {dispatch_async(queue, ^{self.name = [NSString stringWithFormat:@"abcdefghij"];});
}
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (int i = 0; i < 1000; i++) {dispatch_async(queue, ^{self.name = [NSString stringWithFormat:@"abcdefghi"];});
}

第一段代码会crash原因是过度释放,第二段代码没问题。两段代码仅差了一个字符。分别打印两段代码的self.name类型,第一段代码中self.name__NSCFString类型,而第二段代码中为NSTaggedPointerString类型。

第一段代码字符串是__NSCFString存储在堆上,它是个正常对象,需要维护引用计数的。异步并发执行setter方法,可能就会有多条线程同时执行[_name release],连续release两次就会造成对象的过度释放,导致Crash

第二段代码中的NSStringNSTaggedPointerString类型,在objc_release函数中会判断指针是不是TaggedPointer类型,是的话就不对对象进行release操作,也就避免了因过度释放对象而导致的Crash

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

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

相关文章

昇思学习打卡-19-生成式/Pix2Pix实现图像转换

文章目录 网络介绍训练推理结果 网络介绍 Pix2Pix是基于条件生成对抗网络&#xff08;cGAN, Condition Generative Adversarial Networks &#xff09;实现的一种深度学习图像转换模型&#xff0c;可以实现语义/标签到真实图片、灰度图到彩色图、航空图到地图、白天到黑夜、线…

mmdetection

首先下载mmdetection 3.2.0版本的 https://github.com/open-mmlab/mmdetection/tree/v3.2.0 第二步&#xff1a;创建虚拟环境 conda create -n mmdetection python3.8 -y conda activate mmdetection第三步&#xff1a;安装包 pip install torch2.0.1cu118 -f https://downl…

【c++】新领域:“智能数组 ” 问世

引入: 大家有没有发现每次创建和使用数组时很麻烦,因为数组长度一般只能用静态常量,太过局限,不满足大部分开发者的需求。而且遍历数组也很麻烦,又要for循环,又要在其他使用数组的地方检查边界。 于是我就构想了一种“智能数组” 就解决了大部分的难题 这样的语言风格是…

分布式IO系统2通道串口通信模块M602x

现场总线耦合器本身包含一个电源模块&#xff0c;它有 2 个串口通道&#xff0c;通过 Modbus RTU&#xff08;Master&#xff09;协议连接外部串行设备&#xff0c;实现耦合器与外部串行设备通信&#xff0c;现以连接设备的示例带大家了解我们钡铼的2 通道串口通信模块 M602x。…

自闭症孩子为什么容易出现饮食问题?

在星启帆自闭症学校&#xff0c;我们深知自闭症孩子在日常生活中常常面临饮食问题的挑战。这些问题不仅影响孩子的营养摄入和健康成长&#xff0c;也给家庭和学校带来了不小的困扰。以下是我对自闭症孩子容易出现饮食问题的几点分析&#xff1a; 一、感官敏感性 自闭症孩子往往…

【NetTopologySuite类库】合并所有几何的包围盒AABB

流程示意图 示例代码 using GeoAPI.Geometries; using Microsoft.VisualStudio.TestTools.UnitTesting; using NetTopologySuite.Geometries; using NetTopologySuite.IO; using System.Collections.Generic; using System.Linq;namespace Test472 {[TestClass]public class T…

vim网络和安全的操作及shell的使用

目录 vim模式 一般模式下的基本操作&#xff1a; 一般模式切换到编辑模式&#xff1a; 一般模式切换到命令模式&#xff1a; Vim多窗口使用技巧 横向切割打开&#xff1a; 纵向切割打开&#xff1a; 关闭多窗口&#xff1a; 窗口的切换&#xff1a; 网络&#xff1a;…

使用 Flask 3 搭建问答平台(二):User 模型搭建

前言 以下所有代码均是在之前的基础上添加&#xff01;&#xff01;&#xff01; 后面的章节均是如此 知识点 1. 使用 pymysql 模块连接数据库 2. 在模型中创建用户数据表 3. 初始化数据库、创建初始迁移脚本、应用初始迁移脚本 一、User 模型搭建 1.1 准备数据库 1.2 …

Python进阶 异常-包-模块案例

import my_utils.str_util from my_utils import file_utilprint(my_utils.str_util.str_reserves("黑马程序员")) print(my_utils.str_util.substr("itheima",0,4))#文件处理 def print_file_info(file_name):"""将给定路径文件的内容输出…

appium2.0 执行脚本遇到的问题

遇到的问题&#xff1a; appium 上的日志信息&#xff1a; 配置信息 方法一 之前用1.0的时候 地址默认加的 /wd/hub 在appium2.0上&#xff0c; 服务器默认路径是 / 如果要用/wd/hub 需要通过启动服务时设置基本路径 appium --base-path/wd/hub 这样就能正常执行了 方法二…

MongoDB综合实战篇(超容易)

一、题目引入 在MongoDB的gk集合里插入以下数据&#xff1a; 用语句完成如下功能&#xff1a; &#xff08;1&#xff09;查询张三同学的成绩信息 &#xff08;2&#xff09;查询李四同学的语文成绩 &#xff08;3&#xff09;查询没有选化学的同学 &#xff08;4&#xf…

Windows与Ubuntu安装ffmpeg

文章目录 前言ffmpeg的简介安装ffmpegWindows下载设置环境变量 Ubuntu 总结 前言 FFmpeg是一款非常强大的开源音视频处理工具&#xff0c;它包含了众多的音视频编解码库&#xff0c;可以用于音视频的采集、编解码、转码、流化、过滤和播放等复杂的处理。在Windows系统上安装FF…

2024年互联网时代:专业企业IM即时通讯聊天软件的重要性不容忽视!

在这个日新月异的信息时代里&#xff0c;企业IM即时通讯无疑是与我们日常生活联系最紧密的科技工具之一&#xff01;它不仅能让我们轻松实现与亲朋好友间的流畅沟通与联系&#xff0c;更为互联网时代的广大企业员工提供了便捷高效的协同办公平台&#xff0c;助力企业内部信息无…

昂科烧录器支持TI德州仪器的混合信号微控制器MSPM0L1106

芯片烧录行业领导者-昂科技术近日发布最新的烧录软件更新及新增支持的芯片型号列表&#xff0c;其中TI德州仪器的混合信号微控制器MSPM0L1106已经被昂科的通用烧录平台AP8000所支持。 MSPM0L1106微控制器(MCU)属于MSP高度集成的超低功耗32位MSPM0 MCU系列&#xff0c;该系列基…

美式键盘 QWERTY 布局的来历

注&#xff1a;机翻&#xff0c;未校对。 The QWERTY Keyboard Is Tech’s Biggest Unsolved Mystery QWERTY 键盘是科技界最大的未解之谜 It’s on your computer keyboard and your smartphone screen: QWERTY, the first six letters of the top row of the standard keybo…

rust + python+ libtorch

1: 环境&#xff0c;ubuntu 1.1 rust : rust-1.79.0 &#xff08;在官方下载linux版本后&#xff0c;解压文件夹&#xff0c;内部有个install的sh文件&#xff0c;可安装&#xff09; 安装成功测试&#xff1a;cargo --version 1.2 python3.10 (直接使用apt install pytho…

快速排序及归并排序的实现与排序的稳定性

目录 快速排序 一. 快速排序递归的实现方法 1. 左右指针法 步骤思路 为什么要让end先走&#xff1f; 2. 挖坑法 步骤思路 3. 前后指针法 步骤思路 二. 快速排序的时间和空间复杂度 1. 时间复杂度 2. 空间复杂度 三. 快速排序的优化方法 1. 三数取中优化 2. 小区…

30秒学会UML-功能类图

目录 1、类图本体 三部分 修饰符 2、类与类直接关系 泛化关系 实现关系 简单关联关系 依赖关系 组合关系 聚合关系 1、类图本体 三部分 第一层&#xff1a;类名第二层&#xff1a;成员变量&#xff08;类的属性&#xff09;第三层&#xff1a;函数方法&#xff08;类…

手机操作系统的沉浮往事

手机操作系统的沉浮往事&#xff08;上&#xff09; 移动终端操作系统&#xff0c;也就是指手机、平板电脑等设备所使用的操作系统。 在移动互联网高度发达的今天&#xff0c;我们使用移动终端操作系统的时长&#xff0c;可能已经远远超过了 Windows 等桌面操作系统。 那么&…

dp or 数学问题

看一下数据量&#xff0c;只有一千&#xff0c;说明这个不是数学问题 #include<bits/stdc.h> using namespace std;#define int long long const int mo 100000007; int n, s, a, b; const int N 1005;// 2 -3 // 1 3 5 2 -1 // 1 -2 -5 -3 -1 int dp[N][N]; int fun…