• 使用Xcode和Instruments调试解决iOS内存泄露


    http://blog.csdn.net/totogo2010/article/details/8233565

            虽然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 plain copy
     
    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 plain copy
     
    1. [_lastSushiSelected release];  
    2.     _lastSushiSelected = [sushiString retain];  
     

    关于:tableView:cellForRowAtIndexPath

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

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

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

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

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

    容芳志 (http://blog.csdn.net/totogo2010)

    本文遵循“署名-非商业用途-保持一致”创作公用协议

  • 相关阅读:
    Java的静态块与实例块(转)
    Programming Ability Test学习 1031. Hello World for U (20)
    Programming Ability Test学习 1011. World Cup Betting (20)
    Programming Ability Test学习 1027. Colors in Mars (20)
    Programming Ability Test学习 1064. Complete Binary Search Tree (30)
    Programming Ability Test学习 1008. Elevator (20)
    【maven详解-生命周期】Maven的生命周期和插件
    【maven详解-插件】maven插件学习之源码插件Source Xref
    $(document).ready(){}、$(fucntion(){})、(function(){})(jQuery)onload()的区别
    你还没真的努力过,就轻易输给了懒惰
  • 原文地址:https://www.cnblogs.com/zxykit/p/5619957.html
Copyright © 2020-2023  润新知