【iOS】ARC学习
文章目录
- 前言
- 一、autorelease实现
- 二、苹果的实现
- 三、内存管理的思考方式
- __strong修饰符
- 取得非自己生成并持有的对象
- __strong 修饰符的变量之间可以相互赋值
- 类的成员变量也可以使用strong修饰
- __weak修饰符
- 循环引用
- __unsafe_unretained修饰符
- 什么时候使用__unsafe_unretained
- __autoreleasing修饰符
- 访问附有__weak 修饰符的变量时,实际上必定要访问注册到autoreleasepool的对象
- 四、ARC规则
- 不要显式调用dealloc
- 五、属性
前言
在学习ARC之前,先来复习一下内存管理以及autorelease的实现
一、autorelease实现
先来看一下GNUstep源代码:

autorelease其本质就是调用NSAutoreleasePool 对象的addObject 类方法,就是将对象加到自动释放池中

接下来再看一下废弃自动释放池的一些功能函数

二、苹果的实现
可使用showPools输出现在的NSAutoreleasePool的状况输出到控制台
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];id obj = [[NSObject alloc] init];id obj2 = [[NSObject alloc] init];id obj3 = [[NSObject alloc] init];[obj autorelease];[obj2 autorelease];[obj3 autorelease];_objc_autoreleasePoolPrint();[pool drain];

三、内存管理的思考方式
引用计数式内存管理的思考方式就是思考ARC所引起的变化
ARC有效时,id 类型和对象类型同C语言其他类型不同,其类型上必须附加所有权修饰符。 所有权修饰符一共有4 种
(ARC环境下特有)
- __strong修饰符
- __weak修饰符
- __unsafe_unretained修饰符
- __autoreleasing修饰符
__strong修饰符
_ strong修饰符是id类型和对象类型默认的所有权修饰符
也就是说id obj = [[NSObject alloc] init]; = id __strong obj = [[NSObject alloc] init];
再来看下面这段代码
//ARC有效
{id __strong obj = [[NSObject alloc] init]
}
此源代码指定了变量的作用域,当obj超出其变量作用域时, obj会被废弃,同时自动释放其被赋予的对象([[NSObject alloc] init])
而在MRC中,等效的代码为
//ARC无效
{id obj = [[NSObject alloc] init][obj release];
}
因为ARC无效的时候,obj超出变量作用域时,变量并不会被自动废弃,对象也会仍然存在,需要我们手动减少对象的引用计数[obj release]去销毁对象
取得非自己生成并持有的对象
具体如下:

这里需要注意的一点是此处obj确实持有了对象,并且对象的引用计数为1,但是在目前版本的Xcode的MRC环境中,
{id obj = [NSMutableArray array];NSLog(@"%lu", [obj retainCount]);}
在MRC环境下输出的值应该为0,因为array方法表明取得非自己生成并持有的对象,也就是说obj并不持有对象,但是输出如下:

我们来解释一下输出为1的原因:
当我们调用 retainCount 方法时,对于从自动释放池获取的对象,它会临时retained一次,以防止对象被过早释放而导致访问过期数据。
1.array方法创建了一个对象,并将其加到自动释放池中,此时retain count为0
2.obj指向自动释放池中的那个对象,并没有对对象进行retain操作,只是持有了一个指向他的指针
3.调用 [obj retainCount] 时 ,a.编译器会临时保留(retain)对象 b.获得并输出其retain count值 c.释放对象
所以尽管对象最初的 retain count 为 0,但由于 retainCount 方法的实现机制,它会临时保留对象来避免崩溃,这导致我们看到的输出 retain count 为 1。
__strong 修饰符的变量之间可以相互赋值
id __strong obj0 = [[NSObject alloc] init];//对象Aid __strong obj1 = [[NSObject alloc] init];//对象Bid __strong obj2 = nil;obj0 = obj1;//obj0持有有obj1赋值的对象B的强引用,obj0被赋值。所以原先持有的a的强引用失效,此时b的强引用变量为obj1,obj0
由此__strong 修饰符的变量,不仅只在变量作用域中,在赋值上也能够正确地管理其对象的所有者。
类的成员变量也可以使用strong修饰


重点是当Test对象释放时,Test对象的obj_成员变量也会随之被释放
__weak修饰符
使用weak可以使我们取得对象但是并不持有对象
下面有一个代码例子进行解释
id a = [[NSObject alloc] init];id __weak b = a;id c = a;NSLog(@"%lu", CFGetRetainCount((__bridge CFTypeRef)a));NSLog(@"%lu", CFGetRetainCount((__bridge CFTypeRef)c));a = nil;NSLog(@"%lu", CFGetRetainCount((__bridge CFTypeRef)c));NSLog(@"%@", b);NSLog(@"%@", c);c = nil;NSLog(@"%@", b);NSLog(@"%@", c);

可以看到在ARC环境下a,b,c都指向了对象,但是引用计数只有2,这是因为weak是指向对象的指针但并不持有对象,并不会使引用计数加1
当我们将weak修饰符改为strong时,就会出现如下结果
id a = [[NSObject alloc] init];id __strong b = a;id c = a;NSLog(@"%lu", CFGetRetainCount((__bridge CFTypeRef)a));NSLog(@"%lu", CFGetRetainCount((__bridge CFTypeRef)c));a = nil;NSLog(@"%lu", CFGetRetainCount((__bridge CFTypeRef)c));NSLog(@"%@", b);NSLog(@"%@", c);c = nil;NSLog(@"%@", b);NSLog(@"%@", c);

循环引用
内存管理中会发生循环引用的问题,此时就需要用到__weak修饰符

正确的内存释放过程:
首先B对象是A对象的一个属性,也就是A持有B,现在要释放掉A,需要给A发送一个release消息,这时A的引用计数变为0,就要走delloc方法,delloc方法会对A所持有的全部对象发送release消息,当然也包括B,也就是对B进行release,此时B的引用计数也变为0,然后执行delloc,最后A与B都被释放掉了
- (void)dealloc {[_b release]; // 释放持有的 B 实例_b = nil;[super dealloc];
}

循环引用的产生:
解释:对象之间互相持有,形成闭环,导致谁也无法被正确释放
循环引用容易发生内存泄漏。所谓内存泄漏就是应当废弃的对象在超出其生存周期后继续存在。

过程:
- a.例如我们现在想让A释放,也就是让B给A发送release消息,此时B的属性强持有A,所以需要B在delloc方法中对A进行release
- b.我们想要让B执行delloc,就需要就持有B的A对象发送release消息给B
- c.想要A发送release消息给B,就需要A执行delloc方法
- d.想要A执行delloc方法就需要持有A的B对象发送release消息
如此循环往复,对象之间都在等对方给自己发送release消息,导致谁也无法执行,如此往复便造成了循环引用
当然循环引用并不只出现在变量中,还出现在协议与block中,后面在学习的过程中会专门写博客记录
接下来我们谈论一下如何解决这类问题:
因为我们知道weak可以使变量取得但并不持有对象,也就是说不会增加对象的引用计数,我们将对象中的属性用weak修饰符修饰就可以解决这个问题


使用weak时因为变量不持有对象,因此不会造成相互引用,当对象释放后weak变量会自动置为nil,也避免了野指针的情况
#pragma mark 持有对象的弱引用 MRC 在MRC下,没有__weak这样的自动nil置化特性 使用weak持有某对象弱引用时,对象被废弃,弱引用变量自动只为nilid __weak obj1 = nil;{id __strong obj0 = [[NSObject alloc] init];obj1 = obj0;;NSLog(@"%@", obj1);}NSLog(@"%@", obj1);

__unsafe_unretained修饰符
__unsafe_unretained是一个不安全的所有权修饰符,在MRC下使用来避免循环引用

但是与weak相比,其会产生悬垂指针
因此我们在使用**__unsafe_unretained必须保证对象存在**
什么时候使用__unsafe_unretained
- 其与weak相比,可能会有一些更好的性能,追求极致性能便可以使用__unsafe_unretained修饰
- 以及在早版本的iOS中需要使用
__unsafe_unretained来代替__weak. - 比如我们在访问单例或者全局变量时就可以使用这个修饰
__autoreleasing修饰符
ARC中不能使用autorelease方法以及NSAutoreleasePool类,但是实际上ARC有效时autorelease功能还是有作用的
- 指定“@autoreleasepol 块”来替代“NSAutoreleasePool 类对象生成、持有以及废弃”这一范围
@autoreleasepool{
id __autoreleasing obj = [[NSObject alloc] init];
}
- 为对象附加__autoreleasing修饰符代替autorelease方法,等价于在ARC无效时调用
autorelease,即将对象注册到autoreleasepool

但是我们一般不会显式地添加__autoreleasing,因为编译器会检查方法名是否以alloc/new/ copy/mutableCopy 开始,如果不是则自动将返回值的对象注册到autoreleasepool。
例如:
@autoreleasepool f
id __strong obj = [NSMutableArray array];
}
访问附有__weak 修饰符的变量时,实际上必定要访问注册到autoreleasepool的对象
因为weak修饰符只持有对象的弱引用,因此访问引用对象时对象可能被遗弃。所以我们将对象注册到autoreleasepool可以确保对象存在
当将对象注册在autoreleasepool中,autoreleasepool会临时保留这个对象,直到作用域结束
另外在书上讲id *obj = id __autoreleasing *obj,以此类推NSObject **obj便成为了NSObject * _autoreleasing *obj,这里我们需要知道NSObject *__autoreleasing t1与NSObject __autoreleasing *t1有本质的不同:
- 前者指向对象的对象会在被赋值时加入到自动释放池
- 后者常在NSError错误处理中见到
在自动引用计数(ARC)管理的 Objective-C 环境中,当你使用双重指针(比如 NSError **error)作为方法参数时,ARC 会假定这个指针指向的对象是 __autoreleasing
总结来说,NSObject *__autoreleasing t2 是一个自动释放的对象指针,而 NSObject
__autoreleasing *t1 是指向一个自动释放对象指针的指针。
我们以一个代码例子来实验一下
@interface ViewController : UIViewController
@property (nonatomic, weak)NSObject *Obj1;
@property (nonatomic, weak)NSObject *Obj2;@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];[self test1];}- (void)test1 {NSLog(@"%@ %@", self.Obj1, self.Obj2);[self test2];NSLog(@"%@ %@", self.Obj1, self.Obj2);}- (void)test2 {NSObject *t1 = [[NSObject alloc] init];NSObject *__autoreleasing t2 = [[NSObject alloc] init];self.Obj1 = t1;self.Obj2 = t2;NSLog(@"%@ %@", self.Obj1, self.Obj2);}

这段代码中因为test2中,t1超出变量作用域,同时self.obj1是被weak修饰的,并不持有对象,对象超出作用域自动销毁,因此第三行输出null。
而t2由于使用了 __autoreleasing,它的生命周期被延长到当前的自动释放池结束
它的生命周期被延长到当前的自动释放池结束"这句话的含义是:
- 使用__autoreleasing修饰的对象不会在创建它的作用域(通常是一个函数或@autoreleasepool块)结束时被立即释放。
- 相反,这个对象会被自动添加到当前的Autorelease Pool中,延长了它的生命周期。
- 直到当前的Autorelease Pool被销毁时,这个对象才会被最终释放。
@interface ViewController : UIViewController
@property (nonatomic, strong)NSObject *Obj1;
@property (nonatomic, strong)NSObject *Obj2;@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];[self test1];}- (void)test1 {NSLog(@"%@ %@", self.Obj1, self.Obj2);[self test2];NSLog(@"%@ %@", self.Obj1, self.Obj2);}- (void)test2 {NSObject *t1 = [[NSObject alloc] init];NSObject *__autoreleasing t2 = [[NSObject alloc] init];self.Obj1 = t1;self.Obj2 = t2;NSLog(@"%@ %@", self.Obj1, self.Obj2);}

而这段代码中obj用了strong修饰,即使t1,t2作为局部变量超出了作用域,但是self.Obj1仍然持有这个对象,因此这个对象并不会被销毁,因此其仍然存在
四、ARC规则
- 不能使用retain/release/retainCount/autorelease
- 不能使用NSAllocateObject/NSDeallocateObject
- 必须遵守内存管理的方法名规则
- 不要显式调用dealloc
- 使用@autorelease块代替NSAutoreleasePool
- 不能使用区域(NSZone)
- 对象型变量不能作为C语言结构体的成员
- 显式转换id和void*
不要显式调用dealloc
dealloc 方法在大多数情况下还适用于删除已注册的代理或观察者对象。

五、属性

相关文章:
【iOS】ARC学习
文章目录 前言一、autorelease实现二、苹果的实现三、内存管理的思考方式__strong修饰符取得非自己生成并持有的对象__strong 修饰符的变量之间可以相互赋值类的成员变量也可以使用strong修饰 __weak修饰符循环引用 __unsafe_unretained修饰符什么时候使用__unsafe_unretained …...
数据分析 | Matplotlib
Matplotlib 是 Python 中常用的 2D 绘图库,它能轻松地将数据进行可视化,作出精美的图表。 绘制折线图: import matplotlib.pyplot as plt #时间 x[周一,周二,周三,周四,周五,周六,周日] #能量值 y[61,72,66,79,80,88,85] # 用来设置字体样式…...
mac npm install 很慢或报错
npm ERR! code CERT_HAS_EXPIRED npm ERR! errno CERT_HAS_EXPIRED npm ERR! request to https://registry.npm.taobao.org/pnpm failed, reason: certificate has expired 1、取消ssl验证: npm config set strict-ssl false 修改后一般就可以了,…...
100天精通Python(实用脚本篇)——第118天:基于selenium和ddddocr库实现反反爬策略之验证码识别
文章目录 专栏导读一、前言二、ddddocr库使用说明1. 介绍2. 算法步骤3. 安装4. 参数说明5. 纯数字验证码识别6. 纯英文验证码识别7. 英文数字验证码识别8. 带干扰的验证码识别 三、验证码识别登录代码实战1. 输入账号密码2. 下载验证码3. 识别验证码并登录 书籍推荐 专栏导读 …...
51单片机与ARM单片机的区别
51的MCU与ARM的MCU的区别 51单片机与ARM单片机区别主要体现在以下几个方面: 指令集架构(ISA): 51单片机:基于Intel 8051架构,采用的是CISC(复杂指令集计算机)设计,其指令…...
Android 10.0 mtk平台系统添加公共so库的配置方法
1.前言 在10.0的系统定制化开发中,由于 Android对应用应用的系统库限制越来越严格,上层应用包括(apk、jar包)不能直接引用系统的一些so库了。如果需要使用,只能使用,系统申明的公共库。 如果使用非系统申明的公共库,apk运行后调用该so库时,app会直接挂掉,或者系统开发…...
simulink平面五杆机构运动学仿真
1、内容简介 略 68-可以交流、咨询、答疑 2、内容说明 simulink平面五杆机构运动学仿真 [ 摘 要 ] 以 MATLAB 程序设计语言为平台 , 以平面可调五杆机构为主要研究对象 , 给定机构的尺寸参数 , 列出所 要分析机构的闭环矢量方程 , 使用 MATLAB 软件中 SIMULINK 仿真工…...
【Docker】APISIX Ingress Controller部署
APISIX Ingress Controller环境标准软件基于Bitnami apisix-ingress-controller:构建。当前版本为1.8.0 你可以通过轻云UC部署工具直接安装部署,也可以手动按如下文档操作,该项目已经全面开源,可以从如下环境获取 配置文件地址: https://git…...
常见的十大网络安全攻击类型
常见的十大网络安全攻击类型 网络攻击是一种针对我们日常使用的计算机或信息系统的行为,其目的是篡改、破坏我们的数据,甚至直接窃取,或者利用我们的网络进行不法行为。你可能已经注意到,随着我们生活中越来越多的业务进行数字化&…...
接口幂等性问题和常见解决方案
接口幂等性问题和常见解决方案 1.什么是接口幂等性问题1.1 会产生接口幂等性的问题1.2 解决思路 2.接口幂等性的解决方案2.1 唯一索引解决方案2.2 乐观锁解决方案2.3 分布式锁解决方案2.4 Token解决方案(最优方案) 3 Token解决方案落地3.1 token获取、token校验3.2 自定义注解,…...
网站首页添加JS弹屏公告窗口教程
很多小白站长会遇到想给自己的网站添加一个弹屏公告,用于做活动说明、演示站提示等作用与目的。 下面直接上代码:(直接复制到网页头部、底部php、HTML文件中) <script src"https://www.mohuda.com/site/js/sweetalert.m…...
【Rockchip 安10.1 默认给第三方apk默认开启所有权限】
Rockchip 安10.1 默认给第三方apk默认开启所有权限 问题描述解决方法 郑重声明:本人原创博文,都是实战,均经过实际项目验证出货的 转载请标明出处:攻城狮2015 Platform: Rockchip 3229 OS:Android 10.1 Kernel: 4.19 问题描述 有些第三方或者主界面&…...
python-redis缓存装饰器
目录 redis_decorator安装查看源代码使用 redis_decorators安装查看源代码\_\_init\_\_.pycacheable.py 各种可缓存的类型cache_element.py 缓存的元素caching.py 缓存主要逻辑 使用 总结全部代码参考 redis_decorator 安装 pip install redis_decorator查看源代码 from io …...
每个私域运营者都必须掌握的 5 大关键流量运营核心打法!
很多人觉得私域运营比较简单,只是运营的事情,但事实并非如此,私域运营体系非常大,包含了公私域联动、品牌运营、品类战略,它是一个自上而下,由内到外的系统化工程。 很多人天天在想着如何引流拓客…...
蓝桥杯--平均
在编程竞赛,尤其是参与蓝桥杯的过程中,遇到各种问题需求是家常便饭。最近,我遇到了一个非常有趣且颇具挑战性的算法问题。问题描述如下:对于一个长度为n的数组(n是10的倍数),数组中的每个元素均…...
未来已来:科技驱动的教育变革
我们的基础教育数百年来一成不变。学生们齐聚在一个物理空间,听老师现场授课。每节课时长和节奏几乎一致,严格按照课表进行。老师就像“讲台上的圣人”。这种模式千篇一律,并不适用于所有人。学生遇到不懂的问题,只能自己摸索或者…...
【蓝桥杯每日一题】填充颜色超详细解释!!!
为了让蓝桥杯不变成蓝桥悲,我决定在舒适的周日再来一道题。 例: 输入: 6 0 0 0 0 0 0 0 0 1 1 1 1 0 1 1 0 0 1 1 1 0 0 0 1 1 0 0 0 0 1 1 1 1 1 1 1 输出: 0 0 0 0 0 0 0 0 1 1 1 1 0 1 1 2 2 1 1 1 2 2 2 1 1 2 2 2 2 1 1…...
VSCODE的常用插件
1、中文设置 (1)搜索 chinese Chinese (Simplified) Language Pack for Visual Studio Code C/C Extension Pack (2)配置 通过使用“Configure Display Language”命令显式设置 VS Code 显示语言,可以替代默认 UI…...
Oracle常用DBA相关语句
Oracle常用DBA相关语句 1 表空间1.1 创建表空间1.2 删除表空间1.3 收缩表空间1.4 新增表空间文件1.5 查看表空间使用情况1.6 查看表所占用的空间大小 2 表分区2.1 查询表分区的创建情况2.2 查询表时指定分区 3 用户3.1 创建用户3.2 给用户赋权限3.3 删除用户 4 导入导出4.1 导入…...
JavaScript 入门指南(一)简介及基础语法
JavaScript 简介 JavaScript,简称 js,是一种基于对象(object-based)和事件驱动(Event Driven)的简单的并具有安全性能的脚本语言。 JavaScript 特性 JavaScript 是解释性语言,而不是编译性语言…...
第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...
微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...
三维GIS开发cesium智慧地铁教程(5)Cesium相机控制
一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点: 路径验证:确保相对路径.…...
学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...
让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...
CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
漏洞概览 漏洞名称:Apache Flink REST API 任意文件读取漏洞CVE编号:CVE-2020-17519CVSS评分:7.5影响版本:Apache Flink 1.11.0、1.11.1、1.11.2修复版本:≥ 1.11.3 或 ≥ 1.12.0漏洞类型:路径遍历&#x…...
AGain DB和倍数增益的关系
我在设置一款索尼CMOS芯片时,Again增益0db变化为6DB,画面的变化只有2倍DN的增益,比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析: 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...
