RunLoop和autorelease的一道面试题
有这么一道iOS面试题 以下代码有没有什么问题?如果有?如何解决?
for (int i = 0; i < largeNumber; i++) {
NSString *str = [NSString stringWithFormat:@"hello -%04d", i];
str = [str stringByAppendingString:@" - world"];
}
局部释放池和RunLoop释放池的概念:
主线程的RunLoop是默认开启的(视图用[[NSRunLoop currentRunLoop] runUntilDate:[NSDate date]]来停止它,也是做不到的), 每一次消息循环开始的时候会先创建自动释放池,这次循环结束前,会释放自动释放池,然后RunLoop等待下次事件源。 在这个过程中,由RunLoop创建的释放池类似于一个全局的释放池。但是开发者可以任何执行的地方创建释放池,也就是局部的释放池,这时的释放池类似于代码块 当释放池结束的时候会自动释放。因此一般情况下,局部的自动释放池很快就被释放了,而RunLoop释放池会等一次消息循环结束的时候释放。
什么样的对象会交给释放池管理:
返回当前类的实例的类方法创建出来的对象,都是autorelease的,会交给所在的释放池进行管理。 例如创建一个Person类,使用[[self alloc]init]方法创建的对象的管理不会交给它所在的释放池,而是根据引用计数来控制释放的时机, 如果使用[[[self alloc]init] autorelease]创建的对象,会交给所在的释放池管理,控制其释放的时机。
- (void)test{
@autoreleasepool {
Person *p = [[Person alloc] init];
p = nil;
NSLog(@"---");
}
NSLog(@"autorelease结束");
}
执行结果:
Person---dealloc
---
autorelease结束
- (void)test1{
@autoreleasepool {
Person *p = [Person person]; // 内部是[[[self alloc] init] autorelease]
p = nil;
NSLog(@"---");
}
NSLog(@"autorelease结束");
}
执行的结果为:
---
Person---dealloc
autorelease结束
因此自动释放池被销毁或耗尽时会向池中所有使用autorelease创建的对象发送release 消息,释放所有autorelease的对象,而不是所有的对象。
回到面试的问题:
当我们使用for循环创建很多个使用autorelease方式创建的NSString对象的时候,将所有的对象的释放权都交给了RunLoop 的释放池,而RunLoop的释放池会等待这个事件处理之后才会释放,因此就会使对象无法及时释放,堆积在内存造成内存泄露,可以在Debug Navigation 中观察到内存激增。为了验证确实是因为autorelease这种创建方式引起的内存泄露,我做了如下的测试:
int largeNumber = 100000000;
- (void)correctSolution1{
for (int i = 0; i < largeNumber; i++) {
NSString *str = [[NSString alloc] initWithFormat:@"hello -%04d", i];
str = [str stringByAppendingString:@" - world"];
}
}
// stringWithFormat:本质上是调用了alloc + initWithFormat: + autorelease
// 我在本例中将stringWithFormat:方法换成了alloc + initWithFormat:
// 这样做问题就解决了:内存几乎没有变化。反向验证了内存飙升确实是autorelease创建方式造成的。
但是在编写代码的时候我们仍然习惯用类的快速创建方法,而不是alloc+init。也就是说,为了方便写程序,又使用了底层实现是alloc+init+autorelease的快速创建对象的方法(如 stringWithFormat:)。因此解决的方案就是添加局部的释放池,以及时释放内存,如果将局部释放池添加到循环外:
- (void)wrongSolution{
@autoreleasepool {
for (int i = 0; i < largeNumber; i++) {
NSString *str = [NSString stringWithFormat:@"hello -%04d", i];
str = [str stringByAppendingString:@" - world"];
}
}
}
这样显然是没有效果的,释放池需要等循环执行之后再释放内存,这和使用RunLoop创建的释放池没有什么区别。 较好的方案就是每次循环的时候添加一个释放池:
- (void)correctSolution2{
for (int i = 0; i < largeNumber; i++) {
@autoreleasepool {
NSString *str = [NSString stringWithFormat:@"hello -%04d", i];
str = [str stringByAppendingString:@" - world"];
}
}
}
这样每一次循环的结束时都会释放一次内存,因而这个循环全部执行完成时也几乎不消耗内存。
总结是:
做多线程开发时,需要在线程调度方法中手动添加自动释放池,尤其是当执行循环的时候,如果循环内部有使用类的快速创建方法创建的对象, 一定要将循环体放到自动释放池中。
RunLoop和autorelease的一道面试题的更多相关文章
- PHP递归创建多级目录(一道面试题的解题过程)
今天看到一道面试题,要写出一个可以创建多级目录的函数: 我的第一个感觉就是用递归创建,具体思路如下: function Directory($dir){ if(is_dir($dir) || @mkd ...
- 关于Java类加载双亲委派机制的思考(附一道面试题)
预定义类加载器和双亲委派机制 JVM预定义的三种类型类加载器: 启动(Bootstrap)类加载器:是用本地代码实现的类装入器,它负责将 <Java_Runtime_Home>/lib下面 ...
- 一道面试题比较synchronized和读写锁
一.科普定义 这篇博文的两个主角“synchronized”和“读写锁” 1)synchronized 这个同步关键字相信大家都用得比较多,在上一篇“多个线程之间共享数据的方式”中也详细列举他的应用, ...
- 一道面试题:按照其描述要求用java语言实现快速排序
回来想了想,写出了如下的程序: /** * 一道面试题,按照其描述要求进行快速排序(英文的,希望理解是对的..) * 要求:和一般的快速排序算法不同的是,它不是依次交换pivot和左右元素节点(交换2 ...
- <转>一道面试题比较synchronized和读写锁
一.科普定义(原文:http://903497571.iteye.com/blog/1874752) 这篇博文的两个主角“synchronized”和“读写锁” 1)synchronized 这个同步 ...
- 一道面试题与Java位操作 和 BitSet 库的使用
前一段时间在网上看到这样一道面试题: 有个老的手机短信程序,由于当时的手机CPU,内存都很烂.所以这个短信程序只能记住256条短信,多了就删了. 每个短信有个唯一的ID,在0到255之间.当然用户可能 ...
- Java中有关构造函数的一道笔试题解析
Java中有关构造函数的一道笔试题解析 1.详细题目例如以下 下列说法正确的有() A. class中的constructor不可省略 B. constructor必须与class同名,但方法不能与c ...
- 【C#基础知识】静态构造函数,来源于一道面试题的理解
看到园友的一道面试题,很好奇,测试了一下结果. public class A { public static int X=B.Y ; public A() { ++X; } } public clas ...
- 一道笔试题来理顺Java中的值传递和引用传递
题目如下: private static void change(StringBuffer str11, StringBuffer str12) { str12 = str11; str11 = ...
随机推荐
- 关于学习JavaScript 的 高三编程 一些心得(三)
最近在学习高三的 过程中,遇到的了一些 难以理解的问题, 在看到第五章之前都是 OK 的.但是到了 引用类型的时候就有点蒙了. 首先我们看下,引用类型的 解释:[引用类型的值(对象)是引用类型的一个 ...
- 安卓3D游戏-神奇宝贝防御战
我和同学用unity引擎做的,作为软件工程的大作业. 是一个花费金钱抓怪.控制怪物站位.击杀进攻的敌人获得金钱的类似塔防的安卓游戏. 下载地址:http://pan.baidu.com/s/1gdpH ...
- java8入门 错误:找不到或者无法加载主类
如果你也遇上的这个问题,但是如果你的Java版本不是6以上,这个解决方案可能就不适合你... 最近在跟着李兴华老湿的视频<<编程开发入门Java 8>>的学习Java... 但 ...
- JAVA对MySQL数据库的操作
一.导包: 使用JDBC连接MySQL数据库时,首先需要导入一个第三方的JAR包(点击下载),下载解压得到一个JAR包,并导入到JAVA项目中,如下图: 二.DBHelper类: 代码如下: impo ...
- tyvj1013 找啊找啊找GF
描述 "找啊找啊找GF,找到一个好GF,吃顿饭啊拉拉手,你是我的好GF.再见.""诶,别再见啊..."七夕...七夕...七夕这个日子,对于sqybi这种单身的 ...
- [Operate System & Algorithm] 页面置换算法
页面置换算法是什么?我们看一下百度百科对页面置换算法给出的定义:在地址映射过程中,若在页面中发现所要访问的页面不在内存中,则产生缺页中断.当发生缺页中断时,如果操作系统内存中没有空闲页面,则操作系统必 ...
- Mac Pro 资源管理器 Double Commander“个性化设置” 备份
操作系统自带的资源管理器,总是有些别扭的地方,在 Windows 系统下,我一般用 Total Commander(破解版)来作为替代品.现在换为 Mac 系统,自带的 Finer 也不怎么好用,连最 ...
- Daily Scrum Meeting ——FirstDay
一.Daily Scrum Meeting照片 二.Burndown Chart 三.项目进展 1.介绍在github上发布的issues 根据MVC三层模型,我们主要发布的是V层以及M层,C层将根据 ...
- Alpha版本十天冲刺——Day 1
站立式会议 会议总结 队员 今天完成 遇到的问题 明天要做 感想 鲍亮 α版本接口文档初步编写,任务统筹 绘制燃尽图出错 学习http资源访问,服务器请求接口demo测试 作为PM,之前对团队具体要做 ...
- linux常用命令-帮助命令man,whatis,apropos,info,help
man 命令 man 配置文件,注意这里只需要写文件名称就可以了,不能写文件的绝对路径 man既可以查看命令的帮助信息也可以查看配置文件的帮助信息,如果内容太多,可以输入"/内容" ...