【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] -> 1
    p = [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函数也会有危险,因为数组元素所赋值的对象有可能被保留在内存中或是重复被废弃,所以也禁止使用。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

location重定向和nginx代理

文章目录 1 location重定向1.1 概述1.2 rewrite跳转1.3 用例1.4 实验1.4.1 基于域名的跳转1.4.2 基于ip的跳转1.4.3 基于后缀名的跳转 2 nginx的代理2.1 nginx内置变量2.2 正向代理2.2.1 固定正向代理2.2.2 自动代理 2.3 反向代理2.3.1 负载均衡的算法2.3.2 负载均衡的特点2.3.…

【Qt】qt基础

目录 一、使用Qt Creator创建qt项目 二、项目文件解析 三、Qt中创建图形化界面的程序的两种方法 四、对象树 五、Qt中处理打印乱码问题的利器&#xff1a;qDebug() 一、使用Qt Creator创建qt项目 1.选择项目模板 选中第一类模板Application(Qt应用程序&#xff0c;包含普…

CSS在线格式化 - 加菲工具

CSS在线格式化 打开网站 加菲工具 选择“CSS在线格式化” 或者直接访问 https://www.orcc.online/tools/css 输入CSS代码&#xff0c;点击左上角的“格式化”按钮 得到格式化后的结果

java之集合(详细-Map,Set,List)

1集合体系概述 1.1集合的概念 集合是一种容器&#xff0c;用来装数据的&#xff0c;类似于数组&#xff0c;但集合的大小可变&#xff0c;开发中也非常常用。 1.2集合分类 集合分为单列集合和多列集合 Collection代表单列集合&#xff0c;每个元素&#xff08;数据&#xff…

C语言刷题

1. 题目描述 根据给出的三角形3条边a:b.c(a.b,c<100.000)&#xff0c;计算三角形的周长和面积。 输入描述: 一行&#xff0c;三角形3条边(能构成三角形)&#xff0c;中间用一个空格隔开. 输出描述: 一行&#xff0c;三角形周长和面积保留两位小数&#xff0c;中问用一个空…

自动驾驶控制与规划——Project 1: 车辆纵向控制

目录 零、任务介绍一、环境配置1.1 CARLA的配置1.2 Docker Ubuntu 20.04 ROS2 Foxy的配置 二、算法2.1 定速巡航2.2 自适应巡航2.3 离散PID控制 三、代码实现3.1 代码补全3.2仿真验证 零、任务介绍 课程主页 配置Carla仿真器配置carla-ros-bridge补全src\ros-bridge\carla_s…

Linux高并发服务器开发 第一天(Linux的目录结构 cd用法 终端提示符格式)

目录 1.命令解析器&#xff1a;shell 2.LINUX下的目录结构 3.cd的使用 3.1cd 绝对路径 3.2cd 相对路径 3.3cd 回车 3.4cd - 4. 终端提示符格式 1.命令解析器&#xff1a;shell 默认运行与计算机系统终端的 用来解析用户输入命令的工具 内核&#xff1a;操作系统的核…

[SAP ABAP] 序列化与反序列化

1.序列化 序列化表示将ABAP对象类型转成json字符串 我们可以使用方法/ui2/cl_json>serialize实现序列化&#xff0c;可以将ABAP中的内表结构转成json字符串类型 REPORT z437_test_2024.* 自定义数据类型 TYPES: BEGIN OF ty_makt,matnr LIKE makt-matnr, " 物料编号…

【h5py】 提取mat文件中的HDF5格式的数据

h5py 提取mat文件中的HDF5格式的数据 使用纯Python查看数据配合Matlab后&#xff0c;使用Python查看数据 一、使用纯Python查看文件数据内容 原理&#xff1a;当HDF5存储的是struct类型数据&#xff0c;解析时要像一棵树&#xff0c;我们需要逐层次的去解析&#xff0c;直到…

蓝桥杯刷题——day1

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

el-table行合并及合并后序号处理

效果图 <el-tableclass"ncky-detail-table"v-loading"tableLoading"border:data"tableDataVo":span-method"objectSpanMethod"row-key"uniqueFlag":row-class-name"tablerowclassname"><el-table-column…

WordPress酱茄主题 开源版 博客资讯自媒体网站模板

一款免费开源的WordPress主题&#xff0c;主题专为WordPress博客、资讯、自媒体网站而设计 运行环境 支持WordPress版本&#xff1a;5.6 兼容Chrome、Firefox、Safari等主流浏览器 支持设备&#xff1a;响应式布局&#xff0c;不同设备不同展示效果 服务器环境建议&#x…

解析UnityEditor.TransformWorldPlacementJSON数据

Unity拷贝运行时数据的世界Trans信息可以得到json数据&#xff0c;稍微改装一下就可以得到对象数据了&#xff08;没找到官方对应的接口&#xff0c;如果有知道的可以评论区留言&#xff09;&#xff0c;如下图&#xff1a; 代码如下&#xff1a; [MenuItem("Assets/解析…

【YashanDB知识库】同样建表语句,大整型数字在Oracle插入成功,在YashanDB插入失败

本文内容来自YashanDB官网&#xff0c;原文内容请见 https://www.yashandb.com/newsinfo/7610113.html?templateId1718516 问题现象 在YashanDB上执行Oracle同样的建表语句&#xff0c;插入同样的数据&#xff0c;包含大的整型数字&#xff0c;在Oracle执行成功&#xff0c;…

YOLOv5-Backbone模块实现

YOLOv5-Backbone模块实现 &#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客 &#x1f356; 原作者&#xff1a;K同学啊 电脑系统&#xff1a;Windows11 显卡型号&#xff1a;NVIDIA Quadro P620 语言环境&#xff1a;python 3.9.7 编译器&#xff1a…

【Linux探索学习】第二十一弹——文件描述符和输出重定向:深入解析Linux操作系统中的文件描述符与输出重定向的底层机制

Linux学习笔记&#xff1a; https://blog.csdn.net/2301_80220607/category_12805278.html?spm1001.2014.3001.5482 前言&#xff1a; 在上一篇&#xff0c;我们已经讲解过文件描述符的相关问题了&#xff0c;但是今天&#xff0c;由于讲解重定向问题需要更进一步理解文件描…

【Spring】使用@Async注解后导致的循环依赖问题

前言&#xff1a;最近遇到一个问题&#xff0c;使用Async注解将方法设置为异步的时候&#xff0c;出现了循环依赖&#xff08;circular reference&#xff09;问题。 1.问题复现 Service public class A implements AInterface {Autowiredprivate BInterface b;AsyncOverride…

数据结构——ST表

ST表的定义 ST表&#xff0c;又名稀疏表&#xff0c;是一种基于倍增思想&#xff0c;用于解决可重复贡献问题的数据结构 倍增思想 这里列举一个去寻找一个区间内的最大值的例子 因为每次会将将区间增大一倍&#xff0c;所以才被称之为倍增思想 &#xff0c;这种思想十分好用…

深入了解IPv6——光猫相关设定:DNS来源、DHCPv6服务、前缀来源等

光猫IPv6设置后的效果对比图&#xff1a; 修改前&#xff1a; 修改后&#xff1a; 一、DNS来源 1. 网络连接 来源&#xff1a; 从上游网络&#xff08;如运营商&#xff09;获取 IPv6 DNS 信息&#xff0c;通过 PPPoE 或 DHCPv6 下发。 特点&#xff1a; DNS 服务器地址直…

配置mysqld(读取选项内容,基本配置),数据目录(配置的必要性,目录下的内容,具体文件介绍,修改配置)

目录 配置mysqld 读取选项内容 介绍 启动脚本 基本配置 内容 端口号 数据目录的路径 配置的必要性 配置路径 mysql数据目录 具体文件 修改配置时 权限问题 配置mysqld 读取选项内容 介绍 会从[mysqld] / [server] 节点中读取选项内容 优先读取[server] 虽然服务…