长路漫漫,唯剑作伴--Automatic Reference Counting
一、引用计数
在OC中,对象什么时候会被释放?
- 答案是当对象没有被任何变量引用(也可以说是没有指针指向该对象)的时候,就会被释放。
怎么知道对象已经没有被引用了呢?
OC采用引用计数(reference counting)的技术来进行管理:
每个对象都有一个关联的整数,称为引用计数器
当代码需要使用该对象时,则将对象的引用计数加1
当代码结束使用该对象时,则将对象的引用计数减1
当引用计数的值变为0时,表示对象没有被任何代码使用,此时对象将被释放。
内存管理的思考方式:
自己创建的对象自己管理。
不是自己创建的对象,自己也能持有。
不再需要自己持有的对象时,释放。
不是自己持有的对象,不能释放。
总结一句话就是:谁创建,谁释放,谁持有,谁管理。
与之对应的消息发送方法如下:
增加引用计数:alloc、new、copy、mutableCopy(此为生成),retain(此为持有对象)。
减少引用计数:release(此为释放对象)。
释放dealloc(此为废弃对象)
下面通过一个简单的例子说明:
新建Dog类,重写其创建和销毁的方法:
@implementation Dog- (instancetype)init {if (self = [super init]) {NSLog(@"小狗被派出去啦!初始引用计数为 %ld",self.retainCount);}return self;}- (void)dealloc {NSLog(@"小狗回到宠物中心");[super dealloc];} @end
在main方法中创建dog对象,給dog发送消息:
//模拟:宠物中心派出小狗 Dog * dog = [[Dog alloc]init]; //模拟:xiaoming需要和小狗玩耍,需要将其引用计数加1 [dog retain]; NSLog(@"小狗的引用计数为 %ld",dog.retainCount); //模拟:xiaoming不和小狗玩耍了,需要将其引用计数减1 [dog release]; NSLog(@"小狗的引用计数为 %ld",dog.retainCount); //没人需要和小狗玩耍了,将其引用计数减1 [dog release]; //将指针置nil,否则变为野指针 dog = nil;
输出结果为:
[34691:7638855] 初始引用计数为 1 [34691:7638855] 小狗的引用计数为 2 [34691:7638855] 小狗的引用计数为 1 [34691:7638855] 销毁Dog
可以看到,引用计数帮助宠物中心很好的标记了小狗的使用状态,在完成任务的时候及时收回到宠物中心。
思考几个问题:
NSString引用计数问题:
NSString * str = @"hello guys"; NSLog(@"%ld", str.retainCount); // 会发现引用计数为-1,这可以理解为NSString实际上是一个字符串常量,是没有引用计数的(或者它的引用计数是一个很大的值(使用%lu可以打印查看),对它做引用计数操作没实质上的影响)。
赋值不会拥有某个对象:
NSString * name = dog.name; // 这里仅仅是指针赋值操作,并不会增加name的引用计数,需要持有对象必须要发送retain消息。
dealloc:
由于释放对象是会调用dealloc方法,因此重写dealloc方法来查看对象释放的情况,如果没有调用则会造成内存泄露。在上面的例子中我们通过重写dealloc让小狗被释放的时候打印日志来告诉我们已经完成释放。
在上面例子中,如果我们增加这样一个操作:
//没人需要和小狗玩耍了,将其引用计数减1 [dog release]; NSLog(@"%ld",dog.retainCount); // 会发现获取到的引用计数为1,为什么不是0呢? // 这是因为对引用计数为1的对象release时,系统知道该对象将被回收,就不会再对该对象的引用计数进行减1操作,这样可以增加对象回收的效率。
二、自动释放池
思考
现在已经明确了,当不再使用一个对象时应该将其释放,但是在某些情况下,我们很难理清一个对象什么时候不再使用(比如xiaoming和小狗玩耍结束的时间不确定),这可怎么办?
ObjC提供autorelease方法来解决这个问题,当給一个对象发送autorelease消息时,方法会在未来某个时间給这个对象发送release消息将其释放,在这个时间段内,对象还是可以使用的。
那autorelease的原理是什么呢?
原理就是对象接收到autorelease消息时,它会被添加到了当前的自动释放池中,当自动释放池被销毁时,会給池里所有的对象发送release消息。
创建
方法一:使用NSAutoreleasePool来创建
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc]init]; //这里写代码 [pool release];
方法二:使用@autoreleasepool创建
@autoreleasepool {//这里写代码 }
自动释放池创建后,就会成为活动的池子,释放池子后,池子将释放其所包含的所有对象。
以上两种方法推荐第一种,因为将内存交给ObjC管理更高效。
自动释放池什么时候创建?
app使用过程中,会定期自动生成和销毁自动释放池,一般是在程序事件处理之前创建,当然我们也可以自行创建自动释放池,来达到我们一些特定的目的。
自动释放池什么时候销毁?
自动释放池的销毁时间是确定的,一般是在程序事件处理之后释放,或者由我们自己手动释放。
下面举例说明自动释放池的工作流程:
代码
//创建一个自动释放池 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; //模拟:宠物中心派出小狗 Dog * dog = [[Dog alloc]init]; //模拟:xiaoming需要和小狗玩耍,需要将其引用计数加1 [dog retain]; NSLog(@"小狗的引用计数为 %ld",dog.retainCount); //模拟:xiaohong需要和小狗玩耍,需要将其引用计数加1 [dog retain]; NSLog(@"小狗的引用计数为 %ld",dog.retainCount); //模拟:xiaoming确定不想和小狗玩耍了,需要将其引用计数减1 [dog release]; NSLog(@"小狗的引用计数为 %ld",dog.retainCount); //模拟:xiaohong不确定何时不想和小狗玩耍了,将其设置为自动释放 [dog autorelease]; NSLog(@"小狗的引用计数为 %ld",dog.retainCount); //没人需要和小狗玩耍了,将其引用计数减1 [dog release]; NSLog(@"释放池子"); [pool release]; //创建一个自动释放池 @autoreleasepool {//模拟:宠物中心派出小狗Dog * dog = [[Dog alloc]init];//模拟:xiaoming需要和小狗玩耍,需要将其引用计数加1 [dog retain];NSLog(@"小狗的引用计数为 %ld",dog.retainCount);//模拟:xiaohong需要和小狗玩耍,需要将其引用计数加1 [dog retain];NSLog(@"小狗的引用计数为 %ld",dog.retainCount);//模拟:xiaoming确定不想和小狗玩耍了,需要将其引用计数减1 [dog release];NSLog(@"小狗的引用计数为 %ld",dog.retainCount);//模拟:xiaohong不确定何时不想和小狗玩耍了,将其设置为自动释放 [dog autorelease];NSLog(@"小狗的引用计数为 %ld",dog.retainCount);//没人需要和小狗玩耍了,将其引用计数减1 [dog release];NSLog(@"释放池子"); }
结果
[34819:7801589] 初始引用计数为 1 [34819:7801589] 小狗的引用计数为 2 [34819:7801589] 小狗的引用计数为 3 [34819:7801589] 小狗的引用计数为 2 [34819:7801589] 小狗的引用计数为 2 [34819:7801589] 释放池子 [34819:7801589] 销毁Dog // 可以看到,当池子释放后,dog对象才被释放,因此在池子释放之前,xiaohong都可以尽情地和小狗玩耍。
使用自动释放池需要注意:
自动释放池实质上只是在释放的时候給池中所有对象对象发送release消息,不保证对象一定会销毁,如果自动释放池向对象发送release消息后对象的引用计数仍大于1,对象就无法销毁。
自动释放池中的对象会集中同一时间释放,如果操作需要生成的对象较多占用内存空间大,可以使用多个释放池来进行优化。比如在一个循环中需要创建大量的临时变量,可以创建内部的池子来降低内存占用峰值。
autorelease不会改变对象的引用计数。
自动释放池的常见问题
在管理对象释放的问题上,自动帮助我们释放池节省了大量的时间,但是有时候它却未必会达到我们期望的效果,比如在一个循环事件中,如果循环次数较大或者事件处理占用内存较大,就会导致内存占用不断增长,可能会导致不希望看到的后果。
示例代码:
for (int i = 0; i < 100000; i ++) {NSString * log = [NSString stringWithFormat:@"%d", i];NSLog(@"%@", log); }
前面讲过,自动释放池的释放时间是确定的,这个例子中自动释放池会在循环事件结束时释放,那问题来了:在这个十万次的循环中,每次都会生成一个字符串并打印,这些字符串对象都放在池子中并直到循环结束才会释放,因此在循环期间内存不增长。
这类问题的解决方案是在循环中创建新的自动释放池,多少个循环释放一次由我们自行决定。
for (int i = 0; i < 100000; i ++) {@autoreleasepool {NSString * log = [NSString stringWithFormat:@"%d", i];NSLog(@"%@", log);} }
被autorelease处理过的对象的释放时机
autorelease并不是根据作用域来决定释放时机的,而是Runloop。当一个runloop结束时系统才会一次性清理掉被autorelease处理过的对象,其实本质上说是在本次runloop迭代结束时清理掉被本次迭代期间被放到autorelease pool中的对象的。至于何时runloop结束并没有固定的duration!
三、ARC
简介
ARC,自动引用计数,是指iOS的内存管理使用引用计数的技术。
在OC中采用Automatic Reference Counting的机制,让编译器进行内存管理。在新一代的Apple LLVM编译器中设置ARC为有效状态,就不用再次键入retain、release代码,这在降低程序崩溃、内存泄漏等风险的同时,很大程度上减少了开发程序的工作量。编译器完全清楚目标对象,并能立刻释放那些不再被使用的对象(有待斟酌)。如此一来,应用程序将具有可预测性,且运行流畅,速度也将大幅提升。(摘自苹果官方文档说明)
ARC修饰符
__strong:强引用,持有所指向对象的所有权,无修饰符情况下的默认值。如需强制释放,可置nil。
比如我们常用的定时器:
NSTimer * timer = [NSTimer timerWith...];
相当于
NSTimer * __strong timer = [NSTimer timerWith...];
当不需要使用时,强制销毁定时器:
[timer invalidate];
timer = nil;
__weak:弱引用,不持有所指向对象的所有权,引用指向的对象内存被回收之后,引用本身会置nil,避免野指针。
__weak __typeof(self) weakSelf = self;
__autoreleasing:自动释放对象的引用,一般用于传递参数
__unsafe_unretained:为兼容iOS5以下版本的产物,可以理解成MRC下的weak,现在基本用不到,这里不作描述
使用__autoreleasing可能会遇到哪些问题?
使用修饰符的正确姿势
NSString * __weak str = @"hehe"; // 正确! __weak NSString *str = @"hehe"; // 错误!// 我相信很多人都和我一样,从开始用ARC就一直用上面那种错误的写法。 // 那这里就有疑问了,既然文档说是错误的,为啥编译器不报错呢?好吧,是苹果考虑到很多人会用错,所以在编译器这边贴心地帮我们忽略并处理掉了这个错误:) // 虽然不报错,但是我们还是应该按照正确的方式去使用这些修饰符 // 如果你以前也常常用错误的写法,那看到这里记得以后不要这么写了,哪天编译器怒了,再不支持错误的写法,就要郁闷了。 // (参见苹果官方文档)
- sasf
转载于:https://www.cnblogs.com/zhuyiios/p/6710190.html
长路漫漫,唯剑作伴--Automatic Reference Counting相关推荐
- 同学们,长路漫漫伴你闯
终于 最后一批学生也要启程了 不愿说离别伤感话 自古英雄出少年 相处多日 临别之时 无以相送 唯老歌一曲伴你走天涯 (挥手--) <长路漫漫任我闯>林子祥 长路漫漫任我闯 带一身胆色和热肠 ...
- 当我尝试写一个自动写小说的AI,长路漫漫的踩坑之路 ToT
起因 事情是这样的,前几天我在刷B站的时候看到一个大佬用训练了一个自动写高考作文的AI 链接: https://www.bilibili.com/video/BV1pr4y1w7uM 那我就想既然别人 ...
- 长路漫漫,java为伴之java学习路线篇
学习规划篇 作为一名java程序员,你需要了解你学习路线,下面就是规划了!!! 接下来对在实际开发中从上而下用到的各个部分说起: 1.前端 2.网关层 3.服务层 4.数据层 5.大数据 6.搜索引擎 ...
- 亚马逊CEO:Kindle电子阅读器彩色化仍长路漫漫
C114讯 北京时间5月27日午间消息(蒋均牧)亚马逊(Amazon)Kindle电子阅读器的彩色版终会到来,但不会很快. 周二在西雅图举行的年度在线零售商股东会上,亚马逊创始人兼首席执行官杰夫·贝索 ...
- error: 'release' is unavailable: not available in automatic reference counting,该怎么解决
编译出现错误: 'release' is unavailable: not available in automatic reference counting mode.. 解决办法: You nee ...
- Automatic Reference Counting
Automatic Reference Counting http://clang.llvm.org/docs/AutomaticReferenceCounting.html 转载于:https:// ...
- Xcode 4.2 中的Automatic Reference Counting (ARC) (转)
Automatic Reference Counting (ARC), 自动引用计数,是开发Cocoa程序时的一个编译级别的特性,用于自动内存管理. 在XCode 4.2中,使用模板新建一个工程,该工 ...
- Xcode 4.2 中的Automatic Reference Counting (ARC)
Automatic Reference Counting (ARC), 自动引用计数,是开发Cocoa程序时的一个编译级别的特性,用于自动内存管理. 在XCode 4.2中,使用模板新建一个工程,该工 ...
- 错误解决:release' is unavailable: not available in automatic reference counting mode
解决办法: You need to turn off Automatic Reference Counting. You do this by clicking on your project in ...
最新文章
- python 权限控制 linux_16linux的acl的控制权限的介绍
- 《看聊天记录都学不会C语言?太菜了吧》(14)这么神奇?我写了20行代码竟然一行就可以搞定?
- java linkedlist源码分析_LinkedList源码分析(基于Java8)
- 深入学习Spring框架(二)- 注解配置
- 数据访问优化性能(转载)
- 网络安全辅助工具:免费MD5解密网站
- android恢复短信中心号码,短信中心号码怎么查 安卓手机查看短信中心号码方法...
- 宁王比神联手,率锂电池和材料厂,决战锂矿
- 机器学习Class 6:分类及描述
- 遗传算法实现寻找函数最值
- 如何在头条做好影视解说自媒体?
- 【SSH项目实战】国税协同平台-1.项目介绍
- 21天python百度网盘_《21天学通Python》PDF 高清版百度网盘下载
- MySQL约束条件和多表查询方式详解
- 你可曾知道,Java为什么需要虚拟机?
- 浙江仙居“土味”民俗迎中国年:留住乡愁记忆
- 【华为OD机试真题2023 JAVA】网上商城优惠活动(一)
- Excel 去重内部原理知识点详解
- 免密登录远程服务器(适用于命令窗口和VScode)
- python网络爬虫--浏览器伪装
热门文章
- 一个用JavaScript生成思维导图(mindmap)的github repo
- 一位全加器的结构描述vhdl_小学数学结构化学习的评价实践探索
- c语言wb和wb 区别,C语言文件 w+与wb+区别
- wps 模拟分析 规划求解_【网友来稿】利用Excel求解线性规划问题
- yolov3训练自己的数据_YOLOv4 训练自己的数据集
- python pip在哪里_Python 2.7.9(Linux)中的pip在哪里
- python目标检测答案_你好,这里有一份2019年目标检测指南
- php本地测试还是线上,PHP本地与远程测试(一)
- echarts 堆叠柱状图3d效果_【python可视化】:pyecharts:柱形图、堆叠条形图、极坐标堆叠柱形图、极坐标堆叠分类条形图...
- java 三维数组长方体求体积_已知任意一个四面体的六条棱长,如何求出其体积?...