【iOS】——SideTable

news2024/11/24 8:56:05

SideTable

Side Table主要用于存储和管理对象的额外信息,特别是与弱引用相关的数据。Side Table的设计和使用是Objective-C运行时实现弱引用的基础,使得ARC(Automatic Reference Counting)能够正确地处理弱引用的生命周期。

新版本的 objc 中引入了 Tagged Pointer,且 isa 采用 union 的方式进行构造,其中 isa 的结构体中有一个 extra_rchas_sidetable_rc,这两者共同记录引用计数器。

在进行retain操作时,会调用rootRetain方法

objc_object::rootRetain() 方法,只看 extra_rc 超出之后 sidetable 相关的代码,删减之后如下:


uintptr_t carry;
    newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry);  // extra_rc++

if (carry) {
    // Leave half of the retain counts inline and prepare to copy the other half to the side table.
    transcribeToSideTable = true;
    newisa.extra_rc = RC_HALF;
    newisa.has_sidetable_rc = true;
}
if (slowpath(transcribeToSideTable)) {
        // Copy the other half of the retain counts to the side table.
        sidetable_addExtraRC_nolock(RC_HALF);
}

这里主要就是调用了 sidetable_addExtraRC_nolock()

bool 
objc_object::sidetable_addExtraRC_nolock(size_t delta_rc)
{
    assert(isa.nonpointer);
    // 取出this对象所在的SideTable
    SideTable& table = SideTables()[this];
    // 取出SideTable中存储的refcnts,类型为Map
    size_t& refcntStorage = table.refcnts[this];
    // 记录原始的引用计数器
    size_t oldRefcnt = refcntStorage;

    // 容错处理
    assert((oldRefcnt & SIDE_TABLE_DEALLOCATING) == 0);
    assert((oldRefcnt & SIDE_TABLE_WEAKLY_REFERENCED) == 0);
    if (oldRefcnt & SIDE_TABLE_RC_PINNED) return true;

    uintptr_t carry;
    size_t newRefcnt = 
        addc(oldRefcnt, delta_rc << SIDE_TABLE_RC_SHIFT, 0, &carry);
    if (carry) {
        // SideTable溢出处理
        refcntStorage =
            SIDE_TABLE_RC_PINNED | (oldRefcnt & SIDE_TABLE_FLAG_MASK);
        return true;
    } else {
        // SideTable未溢出
        refcntStorage = newRefcnt;
        return false;
    }
}
  1. 首先根据 this,也就是对象的地址从 SideTables 中取出一个 SideTable;
  2. 接着获取 SideTable 的 refcnts,这个成员变量是一个 Map;
  3. 然后存储旧的引用计数器;
  4. 然后进行 add 计算,并记录是否有溢出;
  5. 最后根据是否溢出计算并记录结果,最后返回;

SideTables定义

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

SideTablesde的实质类型是存储SideTable的StripedMap。在StripedMap类中有StripeCount定义存储sidetable的最大数量,所以每个SideTablesdes可以对应多个对象,而每个对象对应一个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 泛型成员,也就是 SideTable 对象。在 array 的下标中,这里使用了 indexForPointer 方法通过位运算计算下标,实现了静态的 Hash Table。而在 weak_table 中,其成员 weak_entry 会将传入对象的地址加以封装起来,并且其中也有访问全局弱引用表的入口。

template<typename T>
class StripedMap {
#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
    enum { StripeCount = 8 };
#else
    enum { StripeCount = 64 };
#endif

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

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

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

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

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

SideTables 可以理解成一个类型为 StripedMap< SideTable> 静态全局对象,内部以数组的形式存储了 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_t
    size_t    num_entries;
    // weak_entry_t数组的count
    uintptr_t mask;
    // hash key 最大偏移值,
    // 采用了开放定制法解决hash冲突,超过max_hash_displacement说明weak_table_t中不存在要找的weak_entry_t
    uintptr_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 中 SideTables() 本质是返回一个 SideTableBuf 对象,该对象存储 8 个 SideTable;

  • 因为涉及到多线程和效率的问题,必定不可能只使用一个 SideTable 来存储对象相关的引用计数器和弱引用;

  • Apple 通过对 object 的地址进行运算之后,对 SideTable 的个数进行取模运算,以此来决定将对象分配到哪个 SideTable 进行信息存储,因为有取模运算,不会出现数组溢出的情况;

  • 当对象需要使用到 sideTable 时,会被分配到 8/64 个全局 sideTables 中的某一个表中存储相关的引用计数器或者弱引用信息;

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

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

相关文章

【深度学习】大模型GLM-4-9B Chat ,微调与部署

下载好东西&#xff1a; 启动容器环境: docker run -it --gpus all --net host --shm-size8g -v /ssd/xiedong/glm-4-9b-xd:/ssd/xiedong/glm-4-9b-xd kevinchina/deeplearning:pytorch2.3.0-cuda12.1-cudnn8-devel-yolov8train bashpip install typer tiktoken numpy1.2…

2024最新版虚拟便携空调小程序源码 支持流量主切换空调型号

产品截图 部分源代码展示 urls.js Object.defineProperty(exports, "__esModule", {value: !0 }), exports.default ["9c5f1fa582bee88300ffb7e28dce8b68_3188_128_128.png", "E-116154b04e91de689fb1c4ae99266dff_960.svg", "573eee719…

web每日一练

每日一题 每天一题罢了。。 ctfshow内部赛签到 扫到备份文件 login.php <?php function check($arr){ if(preg_match("/load|and|or|\||\&|select|union|\|| |\\\|,|sleep|ascii/i",$arr)){echo "<script>alert(bad hacker!)</script>&q…

windows网页视频下载器+Video DownloadHelper+IDM+唧唧down

1:Video DownloadHelper 第一步:下载 链接&#xff1a;https://pan.baidu.com/s/1tWlXcJsq0kY_qrn9pzfCXw?pwdcsy2 提取码&#xff1a;csy2 --来自百度网盘超级会员V4的分享 第二步:浏览器扩展 以edge为例:点击管理扩展: 点击"加载解压缩的扩展": 选中我们的文…

中科亿海微信号采集核心板在振动采集场景中的应用

在工业现场控制领域&#xff0c;对于旋转物体的速度我们通用的做法是测量旋转所产生的振动量来倒推设备的转速值。振动采集系统是一种广泛用于检测和记录系统振动的设备&#xff0c;整体包括传感器和数据采集两部分。传感器类型包括加速度传感器、速度传感器和位移传感器&#…

微信小程序:多图片显示及图片点击放大,多视频显示

微信小程序&#xff1a;多图片显示及图片点击放大&#xff0c;多视频显示 01 多图片显示及图片点击放大02 多视频03 全部代码 01 多图片显示及图片点击放大 <view><view class"title">图片&#xff1a;</view><block wx:if"{{photoUrlList…

什么是离线语音识别芯片?与在线语音识别的区别

离线语音识别芯片是一种不需要联网和其他外部设备支持&#xff0c;‌上电即可使用的语音识别系统。‌它的应用场合相对单一&#xff0c;‌主要适用于智能家电、‌语音遥控器、‌智能玩具等&#xff0c;‌以及车载声控和一部分智能家居。‌离线语音识别芯片的特点包括小词汇量、…

【JavaEE】AQS原理

本文将介绍AQS的简单原理。 首先有个整体认识&#xff0c;全称是 AbstractQueuedSynchronizer&#xff0c;是阻塞式锁和相关的同步器工具的框架。常用的ReentrantLock、Semaphore、CountDownLatch等都有实现它。 本文参考&#xff1a; 深入理解AbstractQueuedSynchronizer只需…

2.5.LeNet

1.LeNet ​ LeNet-5由两个部分组成: 卷积编码器&#xff1a;由两个卷积层组成全连接层密集块&#xff1a;由三个全连接层组成 ​ 先试用卷积层来学习图片空间信息&#xff0c;然后使用全连接层来转换到类别空间 ​ 第一层卷积层要padding一下&#xff0c;收集边框的信息&…

数据清洗系统设计

设计一个高效的数据清洗系统旨在确保数据的质量&#xff0c;以便后续分析和决策过程可以基于准确、一致和完整的信息。以下是设计实时数据清洗系统时需要考虑的关键要素&#xff0c;结合之前提到的设计目标和原则&#xff1a; 1. 高效的数据处理 技术选型&#xff1a;采用并行…

git遇到OpenSSL SSL_read: SSL_ERROR_SYSCALL, errno 0

最简单的方法&#xff0c;直接忽略SSL证书错误就好 一般是代理http/https或者其他问题导致的 直接输入 git config --global http.sslVerify "false" 即可

数学建模学习(2)——决策树

import pandas as pd from sklearn.model_selection import train_test_split from sklearn.tree import DecisionTreeClassifier from sklearn.metrics import accuracy_score dfpd.read_excel(股票客户流失.xlsx) xdf.drop(columns是否流失)#x等于除是否流失这一列以外的数据…

layui+thymeleaf+jquery实现多图片,多视频的上传、预览、放大、编辑功能

layuithymeleafjquery实现多图片&#xff0c;多视频的上传、预览、放大、编辑功能 html: <!--多图片上传--> <div class"layui-row layui-col-space10"><div class"layui-form-item"><div class"layui-form-item layui-form-te…

证书上的服务器名错误解决方法

方法 win r &#xff0c;输入mmc 点击文件——>添加/删除管理单元 找到证书——> 添加 根据自己的存放选择存放位置 点击控制台根节点——> 受信任的根证书颁发机构——>导入 若还出现问题&#xff0c;则参考https://blog.csdn.net/mm120138687/article/details/…

【BUG】已解决:The above exception was the direct cause of the following exception:

The above exception was the direct cause of the following exception: 目录 The above exception was the direct cause of the following exception: 【常见模块错误】 【解决方案】 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c…

uniapp中出现Uncaught runtime errors

项目中运行出现上面的错误信息&#xff0c;使用uniapp发现&#xff0c;其实我只是跨域了&#xff0c;控制台报错&#xff0c;但是不想屏幕上显示&#xff1b; 解决办法是在vue.config.js增加如下配置即可 devServer: {client: {overlay: false,errors:true},}, 错误信息也不想…

求职学习day8

7/21回顾&#xff1a; 用面试鸭的意义可能就在于将知识点用问答的形式具象化在脑海&#xff0c;不然可能只停留在听说过的感觉 7.21 玩了一天。一个很不好的信号。今天下午要试试把 mall 项目的代码运行过一遍。 项目运行问题&#xff1a; 问题 1 &#xff1a;两个门服务器…

Modbus转BACnet/IP网关的技术实现与应用

引言 随着智能建筑和工业自动化的快速发展&#xff0c;不同通信协议之间的数据交换也变得日益重要。Modbus和BACnet/IP是两种广泛应用于自动化领域的通信协议&#xff0c;Modbus以其简单性和灵活性被广泛用于工业自动化&#xff0c;而BACnet/IP则在楼宇自动化系统中占据主导地…

昇思25天学习打卡营第18天| DCGAN生成漫画头像

DCGAN&#xff0c;全称深度卷积对抗生成网络&#xff08;Deep Convolutional Generative Adversarial Networks&#xff09;&#xff0c;是一种通过对抗训练生成图像的技术。它在判别器和生成器中都使用了卷积和转置卷积层。 训练分为两个部分&#xff1a;训练判别器和训练生成…

在spyder中使用arcgis pro的包

历时2天终于搞定了 目标&#xff1a;在anconda中新建一个arcpyPro环境&#xff0c;配置arcgispro3.0中的arcpy 一、安装arcgispro3.0 如果安装完之后打开arcgispro3.0闪退&#xff0c;就去修改注册表&#xff08;在另一台电脑安装arcgispro遇到过&#xff09; 安装成功后可…