内存泄漏检测和处理

来源:互联网 发布:手机淘宝不能照片搜索 编辑:程序博客网 时间:2024/06/06 02:10

一:写在前面

请使用ARC!!!

如果使用的第三方库不支持ARC机制,可以在target->build phase->compile sources中选择对应的源码文件并在气候加入-fno-objc-arc标示,如下图所示:


如果你的应用对内存泄露比较敏感(长时间运行或者耗费较多内存),或者你对代码有洁癖无法忍受半点内存泄露,而你又没有大把的时间和精力取修复那些虚无缥缈的泄漏点,那么ARC将是你最好的选择,因为修复泄露bug实在是太坑爹了...



如果你不习惯ARC,又或者你使用的SDK版本根本不支持ARC,那么,请看下文。


二:修复方法

好,言归正传。

按我个人理解,修复内存泄露可分为三个阶段:


第一阶段:编码

好的编码习惯可以杜绝大部分的内存泄露,具体操作规范可参见我的另一篇博客iOS编程内存管理小结,这也是杜绝内存泄露的最关键阶段。在本阶段多花一些力气放在精确的内存管理上,你将会在调试阶段节省下大把的用于修复泄露的时间与精力。所谓:磨刀不误砍柴工!


第二阶段:静态分析

说白了,静态分析就是使用Xcode自带的Analyze功能(Product-> Analyze),对代码进行静态分析,对于内存泄露(Potential Memory Leak), 未使用局部变量(dead store),逻辑错误(Logic Flaws)以及API使用问题(API-usage)等明确的展示出来,如下图所示:


dead store比较好处理,将从未使用的变量删掉即可。

Memory Leak的提示其实也比较好办,因为其提示已经足够详细,具体到了具体泄露的代码行。找到问题对症下药即可, 忘记release 的加上release,该放autorelease的地方加上auto release。有一个情况需要特别说明一下,见下面所示代码:

[cpp] view plain copy
  1. <span style="font-size:12px;">if (self.dataMember == nil) {  
  2.     self.dataMember = [[NSArray alloc] init];   
  3. }</span>  

这段代码在Analyze的时候也提示有内存泄露,开始我没看出来,后来才发现,等号右边的临时变量却是是泄露了的。正确的写法应该如下所示:

[cpp] view plain copy
  1. <span style="font-size:12px;">if (self.dataMember == nil) {  
  2.     self.dataMember = [[[NSArray alloc] init] autorelease];  
  3. //或者  
  4.     NSArray* temp = [[NSArray alloc] init];  
  5.     self.dataMember = temp;  
  6.     [temp release];   
  7. }</span>  

我比较倾向于第二种写法,到处autorelease对内存占用和性能肯定会有多多少少的影响的。


使用Analyze找到的内存泄露点基本还是准确的,但比较坑人的是Anayze并不能找出所用的泄露点,即使某些极为明显的泄露点使用Analyze也无法分析出来。这时就需要终极大杀器:Instruments登场了。


第三阶段:动态分析

Instruments是Xcode自带的一个强大的应用分析工具,其功能并不局限于内存泄露的分析上,内存占用、CPU使用率等都是其分析的对象,有兴趣的筒子可以仔细研究研究,对提高应用的质量大有益处。

使用Instruments分析内存泄露时,最好使用真机进行调试,笔者就碰到过同一份代码在模拟器上分析有泄露而实体机上就没有的情况,看来不同的CPU架构(x86 vs ARM)对内存的管理还是有一定影响的。

连接实体机到开发机,选择选择运行设备为设备,点击Products-> Profile,经过编译后,会弹出如下对话框:


双击Leak, 进入如下界面:


上面的蓝色条状部分是内存占用情况,而下面的便是内存泄露情况,当下面的状态栏出现粉色条状物体时,恭喜你,泄露出现了...



如果运气比较好的话,下面的提示区域会明确的告诉你那个文件哪个函数有泄露出现,但...很多情况下,出现的只是下面这种无厘头的提示:


这是要闹那样...


当然,上面图中所示的为5.1.1系统中的一个bug,在tableview滚动的过程中会出现一些莫名其妙的泄露点,同样的代码在5.0.1中并不会出现上面的泄露问题。

但这种很难找到泄露根源的问题会很多,寻找这些问题需要耗费大量的时间与精力。


修复泄露有下面两种处理方法:


A: 对于那些非常明显的提示,很简单,按图索骥,根据提示的代码文件函数和泄露的对象类型判断泄露类型进行修复。

B: 对于那些不是很明显的提示,例如提示的泄漏点是一个第三方的库或者某个lib,那么这时就需要回忆一下刚才什么操作之后导致了泄露,然后找到与刚刚的操作相关的代码,在分析泄露的类型,例如是NSString或者NSArray,根据泄露的数据类型来推测具体泄露的代码段。这需要那么一点点的灵感和运气...


三:总结

以我亲身精力来说,调泄露是很烦的一件事,花在上面的时间可能要比你预想的多得多。

所以,正如前面提到的,要么在编码过程中提高内存控制的精确性,要么就一劳永逸,直接使用ARC,要泄露去屎吧!


0 0