本文转载至 http://my.oschina.net/sunqichao/blog?disp=2&p=3

虽然iOS 5.0版本之后加入了ARC机制,由于相互引用关系比较复杂时,内存泄露还是可能存在。所以了解原理很重要。

这里讲述在没有ARC的情况下,如何使用Instruments来查找程序中的内存泄露,以及NSZombieEnabled设置的使用。

本文假设你已经比较熟悉Obj-C的内存管理机制。

实验的开发环境:XCode 4.5.2

1、运行Demo。

先下载一个实现准备好的内存泄露的Demo吧:leak app

下载下来,打开运行,程序是一个寿司的列表,列出各种寿司卷。试着选择里面的几行,应该是选第二行的时候就崩溃了。崩溃截图:

在崩溃的地方断住了,知道crash的地方了,但是不知道具体crash的原因。

2、设置NSZombieEnabled

这是一个 “EXC_BAD_ACCESS”错误。我们打开XCode的选项:“NSZombieEnabled” 。在crash时可能会给你更多的一些提示信息。

设置步骤:1

2:勾上红色框里的

运行,按刚才的操作选中其中的cell。再次crash,这次在output窗口会看到多了一项错误信息:

2012-11-28 13:22:08.911 PropMemFun[2132:11303] *** -[CFString respondsToSelector:]: message sent to deallocated instance 0x713ebc0

大概意思是:向已释放的内存发送消息。也就是说使用了已释放的内存,在C语言相当于使用了“野指针”

看了下crash的这个语句,sushiString应该是没问题的,它是从stringWithFormat初始化出来的。那就是_lastSushiSelected的问题。

_lastSushiSelected指向了sushiString,sushiString是一个autorelease变量。 在第二次点击时,使用的是sushiString已经被释放,所以crash了。那为_lastSushiSelected保留一下,就可以用了。代码修改如下:

[cpp] view plaincopy

  1. <span style="font-size:14px;">    _lastSushiSelected = [sushiString retain];

  2. </span>

运行,这时候不崩溃。

3、分析内存泄露(shift+command+b)

app不crash了,那看看有没有内存泄露。用XCode的Analyze就能分析到哪里有内存泄露

分析之后可以看到:

这里提示alertView没被释放,有内存泄露,那我们释放

[alertView release];

再分析,这个问题解决了。

4、使用Instruments的leaks工具

分析内存泄露不能把所有的内存泄露查出来,有的内存泄露是在运行时,用户操作时才产生的。那就需要用到Instruments了。

按上面操作,build成功后跳出Instruments工具,选择Leaks选项,这时候寿司程序也运行起来了,选中list中的项,拖动等操作后,工具显示效果如下:

大家可能都能猜到,红色的柱子表示内存泄露了。怎么通过这个工具看到在哪泄露了呢?

先在工具栏按下红色的圆形按钮,把工具监视内存的活动停下来。选择Leak,然后点中间十字交叉那,选择Call Tree.

这时候左下角的Call Tree的可选项可以选了。选中Invert Call Tree 和Hide System Libraries,显示如下:

这时候内存泄露的具体代码找到了,在右边的红色框框里指定了哪个方法出现了内存泄露。

你只要在这些方法上双击,就会跳转到具体的代码,哈哈,是不是很方便。

这里应该是提示100%内存会泄露。

6、解决内存泄露问题

问题找到了,那就解决吧

关于:tableView:didSelectRowAtIndexPath ,分析下它的内存过程:

  1. sushiString变量通过autorelease创建,它的引用计数是1.

  2. 这行代码使得引用计数增加到2, _lastSushiSelected = [sushiString retain];

  3. 这个方法结束时,sushiString的autorelease生效了,这个变量的引用计数减少为1

  4. 当再次执行tableView:didSelectRowAtIndexPath这个方法时,_lastSushiSelected被赋值了新指针,老的_lastSushiSelected的引用计数还是1,没有被释放,产生了内存泄露。

怎么解决呢?

在_lastSushiSelected = [sushiString retain];之前把原来的release就ok了:

[cpp] view plaincopy

  1. [_lastSushiSelected release];

  2. _lastSushiSelected = [sushiString retain];

关于:tableView:cellForRowAtIndexPath

这个比较明显,sushiString被alloc和init之后就没有释放,可以用stringWithFormat来调用autorelease,代码如下:

[cpp] view plaincopy

  1. NSString *sushiString = [NSString stringWithFormat:@"%d: %@", indexPath.row, sushiName];

好了,泄露都fix了,再用工具分析看看,这时候你再点,再拖,再怎么操作,都没有内存泄露了。表明内存泄露被堵住了。

这是本文修复好的app代码:no LeakApp

本文参考:http://www.raywenderlich.com/

最新文章

  1. 在PC上测试移动端网站和模拟手机浏览器的5大方
  2. js设置和读取cookie
  3. Linux常用命令学习1---(安装、文件系统、目录操作命令cd ls mv cp rm mkdir、链接命令ln……)
  4. LA 5135 井下矿工
  5. 使用 Intellij Idea 导出JavaDoc
  6. 推荐!国外程序员整理的 PHP 资源大全
  7. python 列表去重(数组)的几种方法
  8. 从客户端(&amp;)中检测到有潜在危险的 Request.Path 值解决方案
  9. Java中的字符串驻留(String Interning)
  10. DOM 文档对象模型
  11. java websocket工具
  12. z3 巧解CTF逆向题
  13. 【tool】VLC播放rtmp协议
  14. mp4文件解析(纯c解析代码)
  15. Win10问题
  16. Kylin的简介与安装部署
  17. Jackson 工具类使用及配置指南
  18. Mac Sublime Text3 如何安装插件
  19. MapServer和GeoServer对比
  20. 657. Judge Route Circle

热门文章

  1. javascript 常用对象
  2. SparkSQL ThriftServer服务的使用和程序中JDBC的连接
  3. 【WPF】ListBox使用UserContrl作为子控件,引入UserContrl界面
  4. java虚拟机和Dalvik虚拟机
  5. Eclipse 中 Debug 模式跳转到 exitCurrentThread 的问题解决
  6. HTML——动画效果回到顶层(小火箭)
  7. 4种用于构建嵌入式linux系统的工具_转
  8. CMake 使用方法
  9. ASP.NET 防止重复提交提示层
  10. /etc/fstab文件损坏怎么办