【iOS】OC高级编程 iOS多线程与内存管理阅读笔记——自动引用计数(四)

目录

ARC规则

规则

对象型变量不能作为C语言结构体的成员

显式转换id和void*

属性

数组


ARC规则

规则

在ARC有效的情况下编译源代码必须遵守一定的规则:

主要解释一下最后两条

对象型变量不能作为C语言结构体的成员

要把对象型变量加入到结构体成员中时,可强制转换为void*或是附加前面所述的__unsafe_unretained修饰符。

显式转换id和void*

ARC无效时,像以下代码这样将id变量强制转换void*变量并不会出问题。

id obj = [[NSObejct alloc] init];
void *p = obj;

更进一步,将该void*变量赋值给id变量中,调用其实例方法,运行时也不会有问题

id o = p;
[o release];

但是在ARC有效时这便会引起编译错误。

id型或对象型变量赋值给void*或者逆向赋值时都需要进行特定的转换。如果只想单纯地赋值,则可以使用"__bridge转换"。

id obj = [[NSObject alloc] init];
void *p = (__bridge void *)obj;
id o = (__bridge id)p;

像这样,通过"__bridge转换",id和void*就能互相转换。

但是转换为void* 的__bridge转换,其安全性与赋值给__unsafe_unretained修饰符相近,甚至会更低。如果管理时不注意赋值对象的所有者,就会因悬垂指针而导致程序崩溃。

__bridge转换中还有另外两种转换,分别是“__bridge_retained转换”和"__bridge_transfer转换"

id obj = [[NSObject alloc] init];
void *p = (__bridge_retained void *)obj;

__bridge_retained转换可使要转换赋值的变量也持有所赋值的对象。下面来看看ARC无效时的源代码是如何编写的

id obj = [[NSObject alloc] init];
void *p = obj;
[(id)p retain];

__bridge_retained转换变为了retain。变量obj和变量p同时持有对象。再来看几个其他的例子。

void *p = 0;
{id obj = [[NSObject alloc] init];p = (__bridge_retained void *)obj;
}
NSLog(@"class=%@", [(__bridge id)p class]);

变量作用域结束时,虽然随着持有强引用的变量obj失效,对象随之释放,但由于__bridge_retained转换使变量p看上去处于持有该对象的状态,因此该对象不会被废弃。下面我们比较一下ARC无效时的代码

void *p = 0;
​
{id obj = [[NSObject alloc] init];//[obj retainCount] -> 1p = [obj retain];//[obj retainCount] -> 2[obj release];//[obj retainCount] ->1
}
//[(id)p retainCount] -> 1
//即 [obj retainCount] -> 1
//对象仍存在
NSLog(@"class=%@", [(__bridge id)p class]);

__bridge_transfer转换提供与此相反的动作,被转换的变量所持有的对象在该变量被赋值给转换目标变量后随之释放。

id obj = (__bridge_transfer id)p;

该源代码在ARC无效时这样表述:

id obj = (id)p;
[obj retain];
[(id)p release];

__bridge_retained转换与retain类似,__bridge_transfer转换与release相似。在给id obj赋值时retain即相当于__strong修饰符的变量。

如果使用以上两种转换,那么不使用id型或对象型变量也可以生成、持有以及释放对象。虽然可以这样做,但是在ARC中不推荐这种方法

void *p = (__bridge_retained void *)[[NSObject alloc] init];
NSLog(@"class=%@", [(__bridge id)p class]);
(void)(__bridge_transfer id)p;

该源代码与ARC无效时的下列源代码相同

//ARC无效
id p = [[NSObject alloc] init];
NSLog(@"class=%@", [p class]);
[p release];

这些转换多用于OC对象与CF对象之间的相互变换中。

OC对象和CF对象的区别很小,不同之处仅仅只在于生成对象的框架不同。可以使用免费桥来实现二者之间的转换("Toll—Free Bridge",这种转换不用使用额外的CPU资源)。

以下函数即Toll—Free Bridge转换的函数,可用于OC对象和CF对象之间的相互变换,即Toll—Free Bridge转换。

属性

当ARC有效时,OC类的属性也会发生变化。

以上各种属性赋值给指定的属性中就相当于赋值给附加各属性对应的所有权修饰符的变量中。只有copy属性不是简单的赋值,它赋值的是通过NSCopying接口的copyWithZone:方法复制赋值源所生成的对象。

并且,在声明类成员变量时,如果同属性声明中的属性不一致则会引起编译错误。比如下面这种情况。

id obj;
@property (nonatomic, weak)id obj;

在声明id型obj成员变量时,定义属性声明为weak,编译器报错。

此时,需要在成员变量的声明中附加__weak修饰符或者使用strong属性来替代weak属性。

数组

将变量作为静态数组使用时,附有__strong,__weak,__autoreleasing修饰符的数组可以在初始化时初始化为nil。

而对于动态数组,NSMutableArray、NSMutableDicitionary、MSMutableSet等容器会恰当地持有追加的对象并为我们管理这些对象。

像这样使用容器虽然更为合适,但在C语言的动态数组中也可以使用附有__strong修饰符的变量,但是要遵守一些事项:

声明动态数组用指针

id __strong *array = nil;

id *类型默认为"id __autoreleasing*类型",所以要显式指定修饰符__strong。并且,附有__strong只保证id型变量被初始化为nil,并不保证附有__strong修饰符的id指针型变量被初始化为nil。

使用类名时如下记述:

NSObject * __strong *array = nil;

其次使用calloc函数确保想分配的附有__strong修饰符变量的容量占有的内存块。

array = (id __strong *)calloc(entries, sizeof(id));

该源代码分配了entries个所需的内存块。由于使用附有__strong修饰符的变量前必须先将其初始化为nil,所以这里使用使分配区域初始化为0的calloc函数来分配内存。不使用calloc函数,在用malloc函数分配内存后可用memset等函数将内存填充为0。

但是,像下面的源代码这样,将nil代入到malloc函数所分配的数组各元素中来初始化是非常危险的。

array = (id __strong *)malloc(sizeof(id) * entries);
for (NSUInteger i = 0; i < entries; ++i)array[i] = nil;

这是因为由malloc函数分配的内存区域没有被初始化为0,因此nil会被赋值给附有__strong修饰符的并被赋值了随机地址的变量中,从而释放一个不存在的对象。在分配内存时推荐使用calloc函数。

像这样,通过calloc函数分配的动态数组就能完全像静态数组一样使用。

array[0] = [[NSObject alloc]];

但是,在动态数组中操作附有__strong修饰符的变量与静态数组有很大差异,需要自己释放所有的元素。在只是简单地使用free函数废弃了数组用内存块的情况下,数组各元素所赋值的对象不能被再次释放,从而引起内存泄漏。这是因为在静态数组中,编译器能根据变量作用域自动插入释放赋值对象的代码,而在动态数组中,编译器不能确定数组的生存周期,所以无从处理。

使用动态数组时,一定要将nil赋值给所有元素中,使得元素所赋值对象的强引用失效,从而释放那些对象。在此之后,使用free函数废弃内存块。

for (NSUInteger i = 0; i < entries; ++i) array[i] = nil;
free(array);

同初始化的注意事项相反,即使用memset等函数将内存填充为0也不会释放所赋值的对象。这非常危险,只会引起内存泄漏。对于编译器,必须明确地使用赋值给附有__strong修饰符变量的源代码。所以请注意,必须将nil赋值给所有数组元素。

并且,memcpy和realloc函数也会有危险,因为数组元素所赋值的对象有可能被保留在内存中或是重复被废弃,所以也禁止使用。

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

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

相关文章

路由引入问题(双点双向路由回馈问题)

简介 总所周知&#xff0c;路由引入import又称路由重分发redistribute&#xff0c;为了解决不同路由协议进程间路由信息不互通而使用的技术&#xff0c;由于不同路由协议的算法、机制、开销等因素的差异&#xff0c;它们之间无法直接交换路由信息。因此&#xff0c;路由引入技…

26. Three.js案例-自定义多面体

26. Three.js案例-自定义多面体 实现效果 知识点 WebGLRenderer WebGLRenderer 是 Three.js 中用于渲染场景的主要类。它支持 WebGL 渲染&#xff0c;并提供了多种配置选项。 构造器 new THREE.WebGLRenderer(parameters) 参数类型描述parametersObject可选参数对象&…

【在Linux世界中追寻伟大的One Piece】HTTP Session

目录 1 -> 引入HTTP Session 1.1 -> 定义 1.2 -> 工作原理 1.3 -> 安全性 1.4 -> 超时和失效 1.5 -> 用途 2 -> 模拟session行为 3 -> 实验测试session 1 -> 引入HTTP Session 1.1 -> 定义 HTTP Session是服务器用来跟踪用户与服务器交…

Docker-Dockerfile、registry

Dockerfile 一、概述 1、commit的局限 很容易制作简单的镜像&#xff0c;但碰到复杂的情况就十分不方便&#xff0c;例如碰到下面的情况&#xff1a; 需要设置默认的启动命令需要设置环境变量需要指定镜像开放某些特定的端口 2、Dockerfile是什么 Dockerfile是一种更强大的镜…

蓝桥杯刷题——day1

蓝桥杯刷题——day1 题目一题干题目解析代码 题目二题干题目解析代码 题目一 题干 给定一个字符串 s &#xff0c;验证 s 是否是 回文串 &#xff0c;只考虑字母和数字字符&#xff0c;可以忽略字母的大小写。本题中&#xff0c;将空字符串定义为有效的 回文串 。 题目链接&a…

【多模态文档智能】OCR-free感知多模态大模型技术链路及训练数据细节

目前的一些多模态大模型的工作倾向于使用MLLM进行推理任务&#xff0c;然而&#xff0c;纯OCR任务偏向于模型的感知能力&#xff0c;对于文档场景&#xff0c;由于文字密度较高&#xff0c;现有方法往往通过增加图像token的数量来提升性能。这种策略在增加新的语言时&#xff0…

如何在 Ubuntu 22.04 上使用 Fail2Ban 保护 SSH

前言 SSH&#xff0c;这玩意儿&#xff0c;简直是连接云服务器的标配。它不仅好用&#xff0c;还很灵活。新的加密技术出来&#xff0c;它也能跟着升级&#xff0c;保证核心协议的安全。但是&#xff0c;再牛的协议和软件&#xff0c;也都有可能被攻破。SSH 在网上用得这么广&…

供应链系统设计-中台系统设计系列(三)- 好中台的标准之稳定原则

概述 在上一篇供应链系统设计-中台系统设计系列&#xff08;二&#xff09;- 好中台的标准之复用原则中&#xff0c;我们以复用原则为主&#xff0c;讨论了以下3点&#xff1a; 前台业务效率提升&#xff1a;好的中台能够显著提高前台业务的效率&#xff0c;通过将前台业务中通…

CTF 攻防世界 Web: FlatScience write-up

题目名称-FlatScience 网址 index 目录中没有发现提示信息&#xff0c;链接会跳转到论文。 目前没有发现有用信息&#xff0c;尝试目录扫描。 目录扫描 注意到存在 robots.txt 和 login.php。 访问 robots.txt 这里表明还存在 admin.php admin.php 分析 在这里尝试一些 sql…

axios请求拦截器和响应拦截器,封装naive-ui的 Loading Bar加载条和useMessage消息提示

接之前的博客设计从0开始边做边学&#xff0c;用vue和python做一个博客&#xff0c;非规范化项目&#xff0c;怎么简单怎么弄&#xff0c;跑的起来有啥毛病解决啥毛病&#xff08;三&#xff09;&#xff0c;目前已经完成了基本的功能demo&#xff0c;但是请求接口不可能每个页…

Blue Ocean 在Jenkins上创建Pipeline使用详解

BlueOcean是Jenkins的一个插件,它提供了一套可视化操作界面来帮助用户创建、编辑Pipeline任务。以下是对BlueOcean中Pipeline操作的详细解释: 一、安装与启动BlueOcean 安装:在Jenkins的“系统管理”->“插件管理”->“可选插件”中搜索“BlueOcean”,然后点击“Ins…

opencv——识别图片颜色并绘制轮廓

图像边缘检测 本实验要用到Canny算法&#xff0c;Canny边缘检测方法常被誉为边缘检测的最优方法。 首先&#xff0c;Canny算法的输入端应为图像的二值化结果&#xff0c;接收到二值化图像后&#xff0c;需要按照如下步骤进行&#xff1a; 高斯滤波。计算图像的梯度和方向。非极…

基础库urllib的使用

学习爬虫&#xff0c;其基本的操作便是模拟浏览器向服务器发出请求&#xff0c;那么我们需要从哪个地方做起呢?请求需要我们自己构造吗?我们需要关心请求这个数据结构怎么实现吗?需要了解 HTTP、TCP、IP层的网络传输通信吗?需要知道服务器如何响应以及响应的原理吗? 可能…

剑指Offer|day4 LCR 004. 只出现一次的数字 II

LCR 004. 只出现一次的数字 II 给你一个整数数组 nums &#xff0c;除某个元素仅出现 一次 外&#xff0c;其余每个元素都恰出现 **三次 。**请你找出并返回那个只出现了一次的元素。 示例 1&#xff1a; 输入&#xff1a;nums [2,2,3,2] 输出&#xff1a;3提示&#xff1a…

Mysql学习笔记之SQL-1

上篇文章我们介绍了Mysql的安装&#xff0c;这篇文章我们介绍Mysql的操作语言SQL 1. 简介 sql全称&#xff08;Structured Query Language&#xff09;是结构化查询语言&#xff0c;操作关系型数据库的编程语言&#xff0c;定义了一套操作关系型数据库统一标准 2. sql分类 …

埃隆马斯克X-AI发布Grok-2大模型,快来体验~

引言 近年来&#xff0c;人工智能技术的快速发展推动了大语言模型的广泛应用。无论是日常生活中的智能助手&#xff0c;还是行业中的自动化解决方案&#xff0c;大语言模型都扮演着越来越重要的角色。2024年&#xff0c;X-AI推出了新一代的大模型——Grok-2&#xff0c;这款模…

PostgreSQL的学习心得和知识总结(一百六十三)|深入理解PostgreSQL数据库之 GUC参数compute_query_id 的使用和实现

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、参考书籍&#xff1a;《PostgreSQL数据库内核分析》 2、参考书籍&#xff1a;《数据库事务处理的艺术&#xff1a;事务管理与并发控制》 3、PostgreSQL数据库仓库…

多线程编程杂谈(上)

问题 线程执行的过程中可以强制退出吗&#xff1f; 主动退出&#xff1f;被动退出&#xff1f; 问题抽象示例 需要解决的问题 g_run 全局变量需要保护吗&#xff1f; 如何编码使得线程中每行代码的执行可被 g_run 控制&#xff1f; 线程代码在被 g_run 控制并 "强制退…

【Git】:企业级开发和多人协作开发啊

目录 多人协作 模拟配置多人协作环境 多人同一分支开发 多人不同分支开发 远程分支删除后的问题 企业级开发模型 系统开发环境 分支设计规范 多人协作 模拟配置多人协作环境 目前&#xff0c;我们所完成的工作如下&#xff1a; 基本完成 Git 的所有本地库的相关操作&#xff…

【Sql优化】数据库优化方法、Explain使用

文章目录 一、金字塔优化模型二、SQL优化的利器&#xff1a;Explain工具1. Explain 的作用2. Explain 的用法 三、SQL优化方法&#xff08;后续文章细讲&#xff09;1. 创建索引减少扫描量2. 调整索引减少计算量3. 索引覆盖4. 干预执行计划5. SQL改写 四、通过 Explain 优化案例…