【原】你真的懂iOS的autorelease吗?
或许这个题目起得有点太高调了,不过我只是想纠正一些童鞋对于autorelease的认识,如果能帮到几个人,那这篇文章也就值得了!当然,高手请绕道
本文主要探讨两个方面:(1)autorelease对象到底是合适被析构的?(2)OC内部是如何处理一个被autorelease掉的对象的?
(1)autorelease对象到底是何时被析构的?
这个问题说难不难,但说简单也不简单。我们还是先看一类熟悉的不能再熟悉的代码吧:
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *localArr = [NSArray arrayWithObject:@"Weng Zilin"];//这是一个局部对象,封装了autorelease方法
4 }
请问,localArr这个局部变量何时被析构呢?很多人会回答:“出了作用域,也就是花括号之后就会被回收”。但遗憾的是,事实并非你想象的那般顺利。下面我通过几行代码向你证明,localArr出了作用于依旧活得好好的:(ARC环境下)
__weak id objTrace;
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *localArr = [NSArray arrayWithObject:@"Weng Zilin"];//这是一个局部对象,封装了autorelease方法
} - (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
NSLog(@"viewWillAppear__localArr:%@", objTrace);
} - (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
NSLog(@"viewWillAppear__localArr:%@", objTrace);
}
在ARC环境下我用一个__weak类型来追踪localArr的释放时机,__weak并不会对localArr增加引用计数,因此不干扰其释放,log显示如下:
我们发现,localArr在viewWillAppear还活着,在DidAppear已经挂了。这说明了一件事:autorelease并不是根据作用域来决定释放时机的。那到底是依据什么呢?答案是:runloop。runloop不在本文讨论范围内,感兴趣的同学请自行查阅资料,传送门点这里。简单说,runloop就是iOS中的消息循环机制,当一个runloop结束时系统才会一次性清理掉被autorelease处理过的对象,其实本质上说是在本次runloop迭代结束时清理掉被本次迭代期间被放到autorelease pool中的对象的。至于何时runloop结束并没有固定的duration!
那么问题来了:iOS的这种基于runloop的内存回收策略有不方便的时候吗?我认为是显然有的。但凡事物总是有两面性的,使用autorelease的确方便,但在一定的情况下会带来性能问题。我们看个例,这个例子转载在我之前的文章:
for (int i = ; i <= ; i ++) {
//1.首先我们获取到需要处理的图片资源的路径
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"PNG"];
//2.将图片加载到内存中,我们使用了alloc关键字,在使用完后,可以手动快速释放掉内存
UIImage *image = [[UIImage alloc] initWithContentsOfFile:filePath];
//3.这一步我们将图片进行了压缩,并得到一个autorelease类型实例
self.image2 = [image imageByScalingAndCroppingForSize:CGSizeMake(, )];
//4.释放掉2步骤的内存
[image release];
}
上述例子看起来没有什么问题,因为一切都是按照MRC的规定做的,可以说是一种“看起来”十分规范的写法。但是主要到image2这个对象了没,赋值给image2对象的临时image对象是一个autorelease类型。实际去跑这段程序会发现,在循环1000次的条件下内存持续上升,因为那个autorelease对象并没有如我们预期般在每次for循环的花括号结束时释放掉!如果从runloop的角度考虑就显得合理了。
那么问题又来了:既然交给runloop处理不放心(runloop其实是有人类的“拖延症”的),那我们可以人工干预autorelease对象的释放时机吗?答案是,欢天喜地,可以的。上文有提到autorelease pool,这是下一个问题要解决的任务,在这里不展开,你只需要知道,一旦一个对象被autorelease,则该对象会被放到iOS的一个池:autorelease pool,其实这个pool本质上是一个stack,扔到pool中的对象等价于入栈。我们把需要及时释放掉的代码块放入我们生成的autorelease pool中,结束后清空这个自定义的pool,主动地让pool清空掉,从而达到及时释放内存的目的。以上述图片处理的例子为例,优化如下:
for (int i = ; i <= ; i ++) { //创建一个自动释放池
NSAutoreleasePool *pool = [NSAutoreleasePool new];//也可以使用@autoreleasePool{domeSomething}的方式
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"PNG"];
UIImage *image = [[UIImage alloc] initWithContentsOfFile:filePath];
UIImage *image2 = [image imageByScalingAndCroppingForSize:CGSizeMake(, )];
[image release];
//将自动释放池内存释放,它会同时释放掉上面代码中产生的临时变量image2
[pool drain];
}
其中对pool的操作也可以等价地使用@autoreleasePool{domeSomeThing;}替代。以上就简要地回答了本文开始处抛出的第一个问题,小结一下就是:释放时机是基于runloop而不是作用域;通过autorelease pool手动干预释放;循环多次时当心要对autorelease进行优化。下面我们开始第二个问题的讨论
(2)一个对象被标记为autorelease后经历了怎么样的过程?
其实我认为这个问题讨论起来更有意思,因为它已经比较底层了。前面提到autorelease对象最终被放到autorelease pool中,那这个pool到底是何方神圣呢?当我们使用@autoreleasepool{}时,编译器实际上将其转化为以下代码:
void *context = objc_autoreleasePoolPush();
// {}中的代码
objc_autoreleasePoolPop(context);//当前runloop迭代结束时进行pop操作
而objc_autoreleasePoolPush与objc_autoreleasePoolPop又是什么呢?他们只是对autoreleasePoolPage的一层简单封装,下面是autoreleasePoolPage的结构,它是C++数据类型,本质是一个双向链表。next就是指向当前栈顶的下一个位置。
里面还有各种参数,不过记住这句话就行:向一个对象发送- autorelease
消息,就是将这个对象加入到当前AutoreleasePoolPage的栈顶next指针指向的位置。
在文章的最后顺便提一下,在iOS中有三种常用的遍历方法:for、forin、enumerateObjectsUsingBlcok。实际使用中大家可能没有感觉到又什么区别,前面两个比较常用,最后一个是iOS特有的遍历方式,但事实上还是有区别的。block版本的遍历方式已经内嵌了@autoreleasepool{}操作,而前面两个没有,这样就意味着使用block版本的遍历方式会使app更加健壮,内存使用效率更加出色,而且,逼格更高,嘿嘿!
这篇文章的讨论就到这里,that`s all.
Reference:
http://blog.sunnyxx.com/
http://www.cnblogs.com/wengzilin/p/3301549.html
http://www.cnblogs.com/xwang/p/3547685.html
=======================================================
原创文章,转载请注明 编程小翁@博客园,邮件zilin_weng@163.com,微信Jilon,欢迎各位与我在C/C++/Objective-C/机器视觉等领域展开交流!
=======================================================
【原】你真的懂iOS的autorelease吗?的更多相关文章
- 你真的懂ajax吗?
前言 总括: 本文讲解了ajax的历史,工作原理以及优缺点,对XMLHttpRequest对象进行了详细的讲解,并使用原生js实现了一个ajax对象以方便日常开始使用. damonare的ajax库: ...
- 你真的懂 ajax 吗?
前言 总括: 本文讲解了ajax的历史,工作原理以及优缺点,对XMLHttpRequest对象进行了详细的讲解,并使用原生js实现了一个ajax对象以方便日常开始使用. damonare的ajax库: ...
- 程序猿修仙之路--数据结构之你是否真的懂数组? c#socket TCP同步网络通信 用lambda表达式树替代反射 ASP.NET MVC如何做一个简单的非法登录拦截
程序猿修仙之路--数据结构之你是否真的懂数组? 数据结构 但凡IT江湖侠士,算法与数据结构为必修之课.早有前辈已经明确指出:程序=算法+数据结构 .要想在之后的江湖历练中通关,数据结构必不可少. ...
- 你真的懂printf么?
自从你进入程序员的世界,就开始照着书本编写着各种helloworld,大笔一挥: printf("Hello World!\n"); 于是控制台神奇地出现了一行字符串,计算机一句温 ...
- [C#] C# 知识回顾 - 你真的懂异常(Exception)吗?
你真的懂异常(Exception)吗? 目录 异常介绍 异常的特点 怎样使用异常 处理异常的 try-catch-finally 捕获异常的 Catch 块 释放资源的 Finally 块 一.异常介 ...
- 【转】was mutated while being enumerated 你是不是以为你真的懂For...in... ??
原文网址:http://www.jianshu.com/p/ad80d9443a92 支持原创,如需转载, 请注明出处你是不是以为你真的懂For...in... ??哈哈哈哈, 我也碰到了这个报错 . ...
- javascript的语法作用域你真的懂了吗
原文:javascript的语法作用域你真的懂了吗 有段时间没有更新了,思绪一下子有点转不过来.正应了一句古话“一天不读书,无人看得出:一周不读书,开始会爆粗:一月不读书,智商输给猪.”.再加上周五晚 ...
- “三次握手,四次挥手”你真的懂吗?TCP
“三次握手,四次挥手”你真的懂吗? mp.weixin.qq.com 来源:码农桃花源 解读:“拼多多”被薅的问题出在哪儿?损失将如何买单? 之前有推过一篇不错的干货<TCP之三次握手四次挥手 ...
- 【转】先说IEnumerable,我们每天用的foreach你真的懂它吗?
[转]先说IEnumerable,我们每天用的foreach你真的懂它吗? 我们先思考几个问题: 为什么在foreach中不能修改item的值? 要实现foreach需要满足什么条件? 为什么Linq ...
随机推荐
- 瀑布式开发、迭代开发、敏捷开发、XP与SCRUM的区别
瀑布式开发.迭代开发,区别[都属于,生命周期模型] 两者都是一种开发模式,就像设计模式一样,考虑的角度不一样,个人感觉谈不到取代一说. 传统的瀑布式开发,也就是从需求到 ...
- ASP.NET页面事件:顺序与回传详解
当页面被提交请求第一个方法永远是构造函数.您可以在构造函数里面初始一些自定义属性或对象,不过这时候因为页面还没有被完全初始化所以多少会有些限制.特别地,您需要使用HttpContext对象.当前可以使 ...
- JavaScript中JSON的处理心得
一门语言用到深处,就避免不了要对数据的类型进行准确判断,并针对其类型做正确处理. 抛开在Web前端环境不谈,从一门独立编程语言的角度来看js,你就会感受到对js中数据类型的理解有多么重要. 禁止直接多 ...
- 基于Typecho CMS框架开发大中型应用
基于Typecho CMS框架开发大中型应用 大中型应用暂且定义为:大于等于3个数据表的应用!汗吧! Typecho原本是一款博客系统,其框架体系有别于市面上一般意义MVC框架,主体代码以自创的Wid ...
- 第 20 章 CSS3 前缀和 rem
学习要点: 1.CSS3 前缀 2.长度单位 rem 主讲教师:李炎恢 本章主要探讨 HTML5 中 CSS 在发展中实行标准化的一些问题,重点探讨 CSS3 中新属性前缀问题和新的单位 rem. 一 ...
- Openwrt笔记-IPv6与启动顺序
之前使用了nat6方案和x3c8021x实现了校园网上网和IPv6连接:但实际使用时经常出现莫名奇妙的问题.IPv6状态要么是无法连接网络,要么是无法连接Internet:经过研究,发现大概是自启动项 ...
- jmeter使用IP欺骗进行压力测试
loadrunner的IP欺骗功能很强大,耐心研究jmeter官方文档,发现在jmeter2.5以上的版本有此功能的实现~ 准备工作: 1.window7一台,安装jdk1.6环境. 2.下载最新 ...
- 接口测试之webservice
什么是Webservice Web service是一个平台独立的,低耦合的,自包含的.基于可编程的web的应用程序,可使用开放的XML(标准通用标记语言下的一个子集)标准来描述.发布.发现.协调和配 ...
- win server 2008 r2 sharepoint 域环境安装经历
环境: 物理机:win7(x64,计算机名字:wyman-pc,ip:192.168.10.102) / sql server 2008 r2(x64) /VM10 虚拟机:win svr 2008 ...
- 自己总结的web前端知识体系大全【欢迎补充】
1. 前言 大约在几个月之前,让我看完了<webkit技术内幕>这本书的时候,突然有了一个想法.想把整个web前端开发所需要的知识都之中在一个视图中,形成一个完整的web前端知识体系,目的 ...