【iOS】ARC实现

ARC由以下工具来实现:

  • clang(LLVM编译器)3.0以上
  • objc4 Objective-C运行时库493.9以上

下面我们,我们将围绕clang汇编输出和objc4库的源代码探究ARC实现

1. __strong修饰符

1.1 赋值给附有__strong修饰符的变量

看下面代码

{id __strong obj = [[NSObject alloc] init];
}

实际上,该源代码可以转换为调用以下的函数。

// 编译器的模拟代码
id obj = objc_msgSend(NSObject, @selector(alloc));
objc_msgSend(obj, @selector(init));
objc_release(obj);

如原源代码所示,2次调用objc_msgSend方法,变量域结束时通过objc_release释放对象。

虽然ARC有效时不能使用release方法,但由此可知编译器自动插入了release。

1.2 使用alloc/new/copy/mutableCopy以外的方法

{id __strong obj = [NSMutableArray array];
}

转换如下:

// 编译器的模拟代码
id obj = objc_msgSend(NSMutableArray, @selector(array));
objc_retainAutoreleasedReturnValue(obj);
objc_release(obj);

objc_retainAutoreleasedReturnValue()函数主要用于最优化程序运行。它用于自己持有(retained)对象的函数,但它持有的对象应为返回注册在autoreleasepool中对象的方法或是函数的返回值。像该源代码这样,在调用alloc/new/copy/mutableCopy以外的方法,由编译器插入该函数。

下面看看NSMutableArray类的array方法的源代码:

+ (id)array {return [[NSMutableArray alloc] init];
}

以下为该源代码的转换:

+ (id)array 
{id obj = objc_msgSend(NSMutableArray, @selector(alloc));objc_msgSend(obj, @selector(init));return objc_autoreleaseReturnValue(obj);
}

objc_autoreleaseReturnValue()函数用于alloc/new/copy/mutableCopy方法以外方法返回对象的实现上。像该源代码这样,返回注册到autoreleasepool中的对象。但是objc_autoreleaseReturnValue函数同objc_autorelease函数不同,一般不仅限于注册对象到autoreleasepool中。

在上例中,objc_autoreleaseReturnValue()函数会检查使用该函数的方法或函数调用方的执行命令列表,如果函数的调用方在调用了方法或函数后紧接着调用objc_retainAutoreleasedReturnValue()函数,那么就不将返回的对象注册到autoreleasepool中,而是直接传递到方法或函数的调用方。

objc_retainAutoreleasedReturnValue()函数与objc_retain函数不同,即便不注册到autoreleasepool中而返回对象,也能正确的获取对象。

通过以上两个函数的协作,可以不将对象注册到autoreleasepool中而直接传递,这一过程达到了最优化。

在这里插入图片描述

2. __weak修饰符

2.1 赋值给附有__weak修饰符的变量;

{id _weak obj1 = obj;
}

假设变量obj附加__strong修饰符且对象被赋值。

// 编译器的模拟代码
id obj1;
objc_initWeak(&obj1, obj);
objc_destroyWeak(&obj1);

objc_initWeak函数将附有__weak修饰符的变量初始化为0后,会将赋值的对象作为参数调用objc_storeWeak函数。

objc_destroyWeak函数将0作为参数调用objc_storeWeak函数。

即前面的源代码与下列源代码相同:

// 编译器模拟代码
id obj1;
obj1 = 0;
objc_storeWeak(&obj1, obj);
objc_storeWeak(&obj1, 0);

objc_storeWeak函数把第二参数的赋值对象作为键值,将第一参数(附有__weak修饰符的变量)的地址注册到weak表中。如果第二参数为0,则把变量的地址从weak表中删除。

weak表与引用计数表相同,作为散列表实现。将废弃对象的地址作为键值进行检索,就能高速地获取对应的__weak修饰符的变量的地址。对于一个键值,可以注册多个变量的地址。

2.2 废弃谁都不持有的对象时,程序的动作;

(1) objc_release
(2) 因为引用计数为0所以执行dealloc
(3) _objc_rootDealloc
(4) object_dispose
(5) objc_destructInstanse
(6) objc_clear_deallocating

objc_clear_deallocating函数的动作如下:

  1. 从weak表中获取废弃对象的地址作为键值;
  2. 将包含在记录中的所有附有__weak修饰符变量的地址,赋值为nil;
  3. 从weak表中删除该记录;
  4. 从引用计数表中删除废弃对象的地址为键值的记录。

通过以上步骤,实现附有__weak修饰符的变量所引用的对象被废弃,则将nil赋值给该变量的功能。

由此可知,如果大量使用附有__weak修饰符的变量,则会消耗相应的CPU资源。所以我们只在需要避免循环引用时使用__weak修饰符。

2.3 将自己生成并持有的对象赋值给附有__weak修饰符的变量中;

{id __weak obj = [[NSObject alloc] init];
}

因为附有__weak修饰符的变量不能持有对象,这时该对象会被释放并被废弃,因此会引起编译器警告。
在这里插入图片描述

我们看看编译器如何处理该源代码。

// 编译器的模拟代码
id obj;
id tmp = objc_msgSend(NSObject, @selector(alloc));
objc_masSend(tmp, @selector(init));
objc_initWeak(&obj, tmp);
objc_release(tmp);
objc_destroyWeak(&object);

虽然自己生成并持有的对象被通过objc_initWeak函数被赋值给附有__weak修饰符的变量中,但是编译器判断其没有持有者,故该对象立即通过objc_release函数被释放和废弃。

2.4 其他

在《Objective-C 高级编程》这本书中描述,在ARC下,使用附有__weak修饰符的变量即是使用注册到autoreleasepool中的对象。实际上,可能由于我手上这一版太老,经过太多版本变迁。经我验证,至少在Xcode13.3.1中,这个说法已不适用。

看如下源代码:

    @autoreleasepool {id obj = [[NSObject alloc] init];{id __weak o = obj;NSLog(@"%@", o);NSLog(@"%@", o);NSLog(@"%@", o);_objc_autoreleasePoolPrint();}}

该源代码运行结果如下:在这里插入图片描述

可以看到虽然多次使用了附有__weak修饰符的变量,但对象并没有被注册到autoreleasepool中。

稍作修改:

    @autoreleasepool {id obj = [[NSObject alloc] init];{id __weak o = obj;id __autoreleasing tmp = o;NSLog(@"%@", o);NSLog(@"%@", o);NSLog(@"%@", o);_objc_autoreleasePoolPrint();}}

运行结果如下:
在这里插入图片描述

可以看到,该对象只在运行id __autoreleasing tmp = o;时被注册到autoreleasepool中。

3. __autoreleasing修饰符

将对象赋值给__autoreleasing修饰符的变量等同于ARC无效时调用对象的autorelease方法。

3.1 赋值给附有__autoreleasing修饰符的变量;

    @autoreleasepool {id __autoreleasing obj = [[NSObject alloc] init];}

可做如下变换:

// 编译器模拟代码
id pool = objc_autoreleasePoolPush();
id obj = objc_msgSend(NSObject, @selector(alloc));
msgSend(obj, @selector(init));
objc_autorelease(obj);
objc_autoreleasePoolPop(pool);

虽然ARC有效和无效时,其在源代码上的表现有所不同,但autorelease的功能完全一样。

3.2 使用alloc/new/copy/mutableCopy以外的方法

    @autoreleasepool {id __autoreleasing obj = [NSMutableArray array];}

可做如下变换:

// 编译器的模拟代码
id pool = objc_autoreleasePoolPush();
id obj = objc_msgSend(NSMutableArray, @selector(array));
objc_retainAutireleasedReturnValue(obj);
objc_autorelease(obj);
objc_autoreleasePoolPop(pool);

虽然持有对象的方法从alloc方法变为objc_retainAutireleasedReturnValue函数,但注册autoreleasepool的方法仍然是objc_autorelease函数。

4. ARC工作原理

ARC 的工作原理大致是这样:当我们编译源码的时候,编译器会分析源码中每个对象的生命周期,然后基于这些对象的生命周期,在合适的地方添加相应的引用计数操作代码retain, release和autorelease。

ARC 是工作在编译期的一种技术方案,这样的好处是:

  1. 编译之后,ARC 与非 ARC 代码是没有什么差别的,所以二者可以在源码中共存。实际上,你可以通过编译参数 -fno-objc-arc 来关闭部分源代码的 ARC 特性。
  2. 相对于垃圾回收这类内存管理方案,ARC 不会带来运行时的额外开销,所以对于应用的运行效率不会有影响。相反,由于 ARC 能够深度分析每一个对象的生命周期,它能够做到比人工管理引用计数更加高效。例如在一个函数中,对一个对象刚开始有一个引用计数 +1的操作,之后又紧接着有一个 -1 的操作,那么编译器就可以把这两个操作都优化掉。

ARC在编译期和运行期做了什么?

  1. 在编译期,ARC会把互相抵消的retain、release、autorelease操作约简。
  2. ARC包含有运行期组件,可以在运行期检测到autorelease和retain这一对多余的操作。为了优化代码,在方法中返回自动释放的对象时,要执行一个特殊函数。

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

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

相关文章

注释气泡图函数(更新)

之前我们写过一个原创可视化函数Dotplot_anno.R,nature级别图表:一个注释气泡热图函数(适用于单细胞及普通数据)。主要解决的问题是1) 单细胞基因可视化分组注释。2) Bulk RNA差异基因热图、气泡图。3) 富集分析结果气泡图展示。这…

ElementUI —— Upload 文件上传

前言&#xff1a; 实现单文件上传&#xff01;此文章仅记录代码如何实现&#xff0c;便于后期项目学习使用&#xff01;&#xff01; 代码实现&#xff1a; <!--********** View ********** --> <el-uploadclass"upload-demo":http-request"UploadFi…

通过concurrently 包,在一个终端中通过npm script运行两个项目

npm script可以通过 && 串行&#xff0c;& 并行执行两个任务&#xff0c;但是不能在同个打开的终端中运行两个项目&#xff0c;可以通过concurrently包解决 安装 npm i concurrently -Dscript命令 "dev":"concurrently \"npm run start\&quo…

Numpy—ndarray对象

NumPy 最重要的一个特点是其N维数组对象 ndarray&#xff0c;它是一系列同类型数据的集合&#xff0c;以 0 下标为开始进行集合中元素的索引。 ndarray 对象是用于存放同类型元素的多维数组。ndarray 中的每个元素在内存中都有相同存储大小的区域。ndarray 内部由以下内容组成…

【分布式训练】基于Pytorch的分布式数据并行训练

基于Pytorch的分布式数据并行训练 动机为什么要并行分布数据&#xff1f;现有资料的不足 Outline整体框架图带解释的最小demo示例没有multiprocessing开启multiprocessing 分布式训练启动方式混合精度训练&#xff08;采用apex&#xff09; 参考资料 简介&#xff1a; 在PyTorc…

【ROS】ROS1人机界面开发:第一个最简ROS+QtGui程序(按钮启动发布者)

【ROS】郭老二博文之:ROS目录 1、创建工程 1)新建工程:Other Project --> ROS Workspace 2)设置工程名称、路径 3)可以通过点击“Browse”来创建目录 注意:使用自带ros插件的qtcreator-ros,无法创建目录、也不能选择目录,这是个bug,因此需要在终端手动创建目录…

密码学学习笔记(十六 ):Schnorr签名算法

交互式零知识证明 零知识证明(ZKP)就是不会将证据泄露给验证者的知识证明。Schnorr身份认证识别协议是一个交互式ZKP&#xff0c;它满足了完备性、可靠性、零知识性。所谓的交互式ZKP方案通常包含3个步骤&#xff08;承诺、挑战和证明&#xff09;&#xff0c;在文献中通常被称…

elastic elasticsearch 源码解析之选主选举过程

选主 选举算法 角色定义 这里的选主为什么提角色? 是因为不同角色在选主中起到不同的作用.master的非voting_only节点不但参与投票同时还可以参与竞选, master 的voting_only角色仅投票不参与竞选,其余角色不参与. 支持的角色 master data data_content data_hot data_wa…

django框架向DRF框架演变过程详解

一、Django框架实现项目查询接口 主要知识点&#xff1a; Django框架视图函数 1、在 Django 项目中创建一个应用&#xff08;如果还没有创建&#xff09;&#xff1a; python manage.py startapp projects 2、在项目的 models.py 文件中定义项目模型 from django.db impor…

React面试题汇总

1、面试官&#xff1a;说说对 React 的理解&#xff1f;有哪些特性&#xff1f; React遵循组件设计模式&#xff0c;使用虚拟 DOM 来有效地操作 DOM&#xff0c;遵循从高阶组件到低阶组件的单向数据流。React 特性有很多&#xff0c;如&#xff1a;JSX 语法、单向数据绑定、虚…

【C++】-stack和queue的具体使用以及模拟实现(dqeue的介绍+容器适配器的介绍)

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树&#x1f388; &#x1f389;作者宣言&#xff1a;认真写好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee✨ &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法&#x1f384; 如 果 你 …

Java八股文总结(二)

Java八股文总结&#xff08;续&#xff09; 接上篇笔记&#xff1a;Jhttps://blog.csdn.net/weixin_44780078/article/details/130192373 文章目录 Java八股文总结&#xff08;续&#xff09;六、MySql 相关1. InnoDB 与 MyISAM 的区别&#xff1f;2. 为什么 InnoDB 存储引擎表…

kubesphere部署kafka单点

zookeeper 存储: #zk-cm nfsRWO /bitnami/zookeeper 配置: 秘钥: 服务: 有状态服务 名称: zookeeper 镜像: bitnami/zookeeper:3.8.0 副本数量: 环境变量: - TZAsia/Shanghai - ZOO_SERVER_ID1 - ZOO_PORT_NUMBER2181 - ZOO_HEAP_SIZE128 - ALLOW_ANONYMOUS_LOGINyes 启动…

JavaFx 用户界面控件2——ListView

1.列表显示ListView 下面是一个JavaFX ListView的示例代码和使用方法&#xff1a; public class ListViewExample extends Application {Overridepublic void start(Stage primaryStage) {// 创建一个可观察的列表&#xff0c;用于存储ListView中的数据ObservableList<Str…

【MongoDB】SpringBoot整合MongoDB

【MongoDB】SpringBoot整合MongoDB 文章目录 【MongoDB】SpringBoot整合MongoDB0. 准备工作1. 集合操作1.1 创建集合1.2 删除集合 2. 相关注解3. 文档操作3.1 添加文档3.2 批量添加文档3.3 查询文档3.3.1 查询所有文档3.3.2 根据id查询3.3.3 等值查询3.3.4 范围查询3.3.5 and查…

UML与SYSML的关系

UML与SysML的联系 UML&#xff08;统一建模语言&#xff09;和SysML&#xff08;系统建模语言&#xff09;是两种与建模相关的语言&#xff0c;它们之间存在联系和区别。 SysML的图分类如下图所示。 联系 SysML是基于UML的&#xff0c;它重用了UML 2的子集&#xff0c;并提…

wordpress后台的路径都是空白,进不了后台怎么办?

主题或插件冲突&#xff1a;某些主题或插件可能与其他已安装的主题或插件不兼容&#xff0c;导致登录页面显示为空白。您可以通过禁用所有插件和将主题更改为默认主题来查看是否解决了问题。如果解决了问题&#xff0c;则可以逐个重新启用插件以找出是哪个插件造成冲突。 PHP错…

CGT Asia嘉年华|3D细胞培养与类器官研发峰会10月广州召开

类器官指利用成体干细胞或多能干细胞进行体外三维&#xff08;3D&#xff09;培养而形成的具有一定空间结构的组织类似物&#xff0c;是近10年来干细胞领域发展最快的研究热点之一。2022年&#xff0c;FDA 通过现代化法案 2.0&#xff0c;批准全球首个完全基于“类器官芯片”研…

Win11中的Swapfile.sys

除了 pagefile.sys 和 hiberfil.sys 文件外&#xff0c;在系统根目录会多出一个 swapfile.sys 虚拟内存文件。Windows 10/8 系统为什么会同时使用 SWAP 交换文件和 Page 页面文件呢&#xff1f; 其实 swapfile.sys 文件目前只被用来交换 Universal App (其实就是Metro App)的个…

线程、并发、多线程并发

单线程并发 单线程并发&#xff0c;并非真正意义上的单线程。而是只有单一的用户线程。还包括数据库&#xff0c;socket等系统多线程。 单个用户线程&#xff1a;对于十万个用户同时访问服务器&#xff0c;有两种方式处理并发。 为每个用户开一个新的用户线程&#xff0c;每个…