当前位置: 首页 > news >正文

「iOS」————SideTable

iOS学习

  • 前言
  • sideTable
    • SlideTables
    • SideTableBuf
    • SideTable


前言

我们在上一篇中,简单的介绍了weak的实现原理。其中弱引用表就是存储在SideTable中的,这里我们来学习了解一下SideTable

sideTable

sideTable主要用于存储和管理对象的额外信息,特别是弱引用相关的数据。该表的设计和使用时OC运行时实现弱引用的基础,使得ARC能够正确的处理弱引用的生命周期。

SlideTables

定义

static StripedMap<SideTable>& SideTables() {return *reinterpret_cast<StripedMap<SideTable>*>(SideTableBuf);
}

SideTables的实质类型时StripedMap。在StripedMap类中有StripeCount定义存储sidetable的最大数量。所以每个SideTable可以对应多个对象,而每个对象对应一个sideTable。

img

SideTableBuf

// We cannot use a C++ static initializer to initialize SideTables because
// libc calls us before our C++ initializers run. We also don't want a global 
// pointer to this struct because of the extra indirection.
// Do it the hard way.alignas(StripedMap<SideTable>) static uint8_t SideTableBuf[sizeof(StripedMap<SideTable>)];
  • SideTables 在 C++ 的 initializers 函数之前被调用,所以不能使用 C++ 初始化函数来初始化 SideTables,而 SideTables 本质就是 SideTableBuf;
  • 不能使用全局指针来指向这个结构体,因为涉及到重定向问题;

而SideTableBuf本质上就是一个长度为Sizeof(StripedMap)的char类型的数组;所以有:

SideTableBuf 本质上就是一个大小为和 StripedMap<SideTable> 对象一致的内存块;

这也是为什么 SideTableBuf 可以用来表示 StripedMap<SideTable> 对象。本质上而言,SideTableBuf 就是指一个 StripedMap<SideTable>对象;

StripedMap < SideTable >

StripedMap是一个模板类,该类中有一个array成员,用来存储PaddedT对象,并且其中对于[]符号的重载定义中,会返回这个PaddedT的value成员,这个value就是我们传入的T泛型成员,也就是Side Table对象。在array的下标中,这里使用了indexForPointer方法通过位运算计算下标,实现了静态的Hash Table。而在weak_table中,其成员weak_entry会将传入对象的地址加以封装起来,并且其中也有访问全局弱引用表的入口。

T模板类型参数(泛型),它代表 “任意类型”,具体类型由使用 StripedMap 时指定。此处就是SideTable

  • T:泛型参数,在运行时中实际代表 SideTable,让 StripedMap 成为管理 SideTable 的通用容器。
  • PaddedT:对 T(即 SideTable)的包装,通过内存对齐(alignas)避免多线程访问时的 CPU 缓存冲突,提升性能。
template<typename T>
class StripedMap {
#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATORenum { StripeCount = 8 };
#elseenum { StripeCount = 64 };
#endifstruct PaddedT {T value alignas(CacheLineSize);};PaddedT array[StripeCount];static unsigned int indexForPointer(const void *p) {uintptr_t addr = reinterpret_cast<uintptr_t>(p);return ((addr >> 4) ^ (addr >> 9)) % StripeCount;}public:T& operator[] (const void *p) { return array[indexForPointer(p)].value; }const T& operator[] (const void *p) const { return const_cast<StripedMap<T>>(this)[p]; }...省略了对象方法...
}
  • 首先根据是否为 iphone 定义了一个 StripeCount,iphone 下为 8;即最多为八个sidetable

  • 源码中 CacheLineSize 为 64,使用 T 定义了一个结构体,而 T 就是 SideTable 类型;

  • 生成了一个长度为 8 类型为 SideTable 的数组;

  • indexForPointer() 逻辑为根据传入的指针,经过一定的算法,计算出一个存储该指针的位置,因为使用了取模运算,所以值的范围是 0 ~ (StripeCount-1),所以不会出现数组越界;

  • 后面的 operator 表示重写了运算符 [] 的逻辑,调用了 indexForPointer() 方法,这样使用起来更像一个数组;

SideTables可以理解成一个类型为StripeMap< Side Table>静态全局对象,内部以数组的形式存储了StripeCount个SideTable

SideTable

struct SideTable {
// 保证原子操作的自旋锁    spinlock_t slock;
// 引用计数的 hash 表RefcountMap refcnts;
// weak 引用全局 hash 表weak_table_t weak_table;//构造函数SideTable() {memset(&weak_table, 0, sizeof(weak_table));}//析构函数~SideTable() {_objc_fatal("Do not delete SideTable.");}...省略对象方法...
}

slock是一个自旋锁,就是为了保证多线程访问安全性

refcnts本质是一个存储对象引用计数的hash表,key为对象,value为引用计数(优化过得isa中,引用计数主要存在isa中)

weak_table是存储对象弱引用的一个结构体,该结构体内的成员如下

/**全局的弱引用表, 保存object作为key, weak_entry_t作为value* The global weak references table. Stores object ids as keys,* and weak_entry_t structs as their values.*/
struct weak_table_t {// 保存了所有指向特地对象的 weak指针集合weak_entry_t *weak_entries;// weak_table_t中有多少个weak_entry_tsize_t    num_entries;// weak_entry_t数组的countuintptr_t mask;// hash key 最大偏移值,// 采用了开放定制法解决hash冲突,超过max_hash_displacement说明weak_table_t中不存在要找的weak_entry_tuintptr_t max_hash_displacement;
};

下面是refcnts的定义:

typedef objc::DenseMap<DisguisedPtr<objc_object>,size_t,RefcountMapValuePurgeable> RefcountMap;

DenseMap 是一个 hash Map,基类 DenseMapBase 中的部分代码,如下,DenseMapBase中重写了操作符 []:

ValueT &operator[](const KeyT &Key) {return FindAndConstruct(Key).second;}

通过传入的 Key 寻找对应的 Value。而 Key 是 DisguisedPtr<objc_object> 类型,Value 是 size_t 类型。即使用 obj.address :refCount 的形式来记录引用计数器;

回到最初的 sidetable_addExtraRC_nolock 方法中:

size_t& refcntStorage = table.refcnts[this];

通过 this ,即 object 对象的地址,取出 refcnts 这个哈希表中存储的引用计数器;

refcnts 可以理解成一个 Map,使用 address:refcount 的形式存储了很多个对象的引用计数器;看不太懂这里

总结一下吧

img

  • iphone中Side Tables()本质上返回一个Side TableBuf对象,该对象存储8个SideTable;(StripeCount)
  • 涉及到多线程和效率问题,有多个SideTable来存储对象相关的引用计数器和弱引用
  • Apple通过对object的地址进行运算之后,对Side Table的个数进行取模运算,以次来决定将对象分配到哪个SideTable进行信息存储,因为有取模运算,所以不会出现数组溢出。范围为0-StripeCount-1
  • 当对象需要使用到Side Table时,会被分配到到 8/64 个全局 sideTables 中的某一个表中存储相关的引用计数器或者弱引用信息;

这里再附上一张上一篇的弱引用表关系图:
在这里插入图片描述

http://www.dtcms.com/a/311408.html

相关文章:

  • 基于Dockerfile 部署一个 Flask 应用
  • WAIC引爆AI,智元机器人收购上纬新材,Geek+上市,157起融资撑起热度|2025年7月人工智能投融资观察 · 极新月报
  • 【传奇开心果系列】Flet框架流式输出和实时滚动页面的智能聊天机器人自定义模板
  • github在界面创建tag
  • 性能测试-性能测试中的经典面试题二
  • 超级人工智能+无人机操控系统,振兴乡村经济的加速器,(申请专利应用),严禁抄袭!
  • spring-ai-alibaba 学习(十九)——graph之条件边、并行节点、子图节点
  • linux编译基础知识-库文件标准路径
  • Docker 的网络模式
  • 3 使用 Jenkins 构建镜像:将你的应用打包成镜像
  • 【20min 急速入门】使用Demucs进行音轨分离
  • ffmpeg命令和ffplay命令详解
  • Java高性能编程实践指南
  • ARM Cortex-M异常处理高级特性详解
  • OpenCV 全解读:核心、源码结构与图像/视频渲染能力深度对比
  • [硬件电路-121]:模拟电路 - 信号处理电路 - 模拟电路中常见的难题
  • 网络编程之原始套接字
  • Anthropic:跨越生产效能拐点的AI增长飞轮
  • [硬件电路-123]:模拟电路 - 信号处理电路 - 常见的高速运放芯片、典型电路、电路实施注意事项
  • 淘宝小程序的坑
  • 阿里云部署微调chatglm3
  • 音视频学习(四十七):模数转换
  • 文心4.5开源测评:国产大模型的轻量化革命与全栈突破
  • Unity_数据持久化_C#处理XML文件
  • Ubuntu18网络连接不上也ping不通网络配置问题排查与解决方法
  • Pyspark的register方法自定义udf函数
  • Android13文件管理USB音乐无专辑图片显示的是同目录其他图片
  • JVM 02 垃圾回收
  • PyTorch基础 :三角函数与特殊运算
  • 隧道照明“隐形革命”:智能控制如何破解安全与节能双重命题