内存泄漏问题的解决
内存泄漏(MemoryLeaks)是当一个对象或变量在使用完成后没有释放掉,这个对象一直占有着这块内存,直到应用停止。如果这种对象过多内存就会耗尽,其它的应用就无法运行。这个问题在C++、C和Objective-C的MRR中是比较普遍的问题。
在Objective-C中释放对象的内存是发送release和autorelease消息,它们都是可以将引用计数减1,当为引用计数为0时候,release消息会使对象立刻释放,autorelease消息会使对象放入内存释放池中延迟释放。
上代码:
<code style="padding:0px; font-family:Menlo,Monaco,Consolas,'Courier new',monospace; color:inherit; background-color:transparent; border:0px"><span style="color:#000000;">-</span><span style="color:#000000;"> </span><span style="color:#000000;">(</span><span style="color:#008b;">void</span><span style="color:#000000;">)</span><span style="color:#000000;">viewDidLoad </span><span style="color:#000000;">{</span><span style="color:#000000;"> </span><span style="color:#000000;">[</span><span style="color:#008b;">super</span><span style="color:#000000;"> viewDidLoad</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:#2b91af;">NSBundle</span><span style="color:#000000;"> </span><span style="color:#000000;">*</span><span style="color:#000000;">bundle </span><span style="color:#000000;">=</span><span style="color:#000000;"> </span><span style="color:#000000;">[</span><span style="color:#2b91af;">NSBundle</span><span style="color:#000000;"> mainBundle</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:#2b91af;">NSString</span><span style="color:#000000;"> </span><span style="color:#000000;">*</span><span style="color:#000000;">plistPath </span><span style="color:#000000;">=</span><span style="color:#000000;"> </span><span style="color:#000000;">[</span><span style="color:#000000;">bundle pathForResource</span><span style="color:#000000;">:@</span><span style="color:#8000;">"team"</span><span style="color:#000000;"> ofType</span><span style="color:#000000;">:@</span><span style="color:#8000;">"plist"</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:gray;">//获取属性列表文件中的全部数据</span><span style="color:#000000;"> </span><span style="color:#008b;">self</span><span style="color:#000000;">.</span><span style="color:#000000;">listTeams </span><span style="color:#000000;">=</span><span style="color:#000000;"> </span><span style="color:#000000;">[[</span><span style="color:#2b91af;">NSArray</span><span style="color:#000000;"> alloc</span><span style="color:#000000;">]</span><span style="color:#000000;"> initWithContentsOfFile</span><span style="color:#000000;">:</span><span style="color:#000000;">plistPath</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:#000000;">}</span><span style="color:#000000;"> </span><span style="color:#000000;">-</span><span style="color:#000000;"> </span><span style="color:#000000;">(</span><span style="color:#2b91af;">UITableViewCell</span><span style="color:#000000;"> </span><span style="color:#000000;">*)</span><span style="color:#000000;">tableView</span><span style="color:#000000;">:(</span><span style="color:#2b91af;">UITableView</span><span style="color:#000000;"> </span><span style="color:#000000;">*)</span><span style="color:#000000;">tableView cellForRowAtIndexPath</span><span style="color:#000000;">:</span><span style="color:#000000;"> </span><span style="color:#000000;">(</span><span style="color:#2b91af;">NSIndexPath</span><span style="color:#000000;"> </span><span style="color:#000000;">*)</span><span style="color:#000000;">indexPath </span><span style="color:#000000;">{</span><span style="color:#000000;"> </span><span style="color:#008b;">static</span><span style="color:#000000;"> </span><span style="color:#2b91af;">NSString</span><span style="color:#000000;"> </span><span style="color:#000000;">*</span><span style="color:#2b91af;">CellIdentifier</span><span style="color:#000000;"> </span><span style="color:#000000;">=</span><span style="color:#000000;"> </span><span style="color:#000000;">@”</span><span style="color:#2b91af;">CellIdentifier</span><span style="color:#000000;">”;</span><span style="color:#000000;"> </span><span style="color:#2b91af;">UITableViewCell</span><span style="color:#000000;"> </span><span style="color:#000000;">*</span><span style="color:#000000;">cell </span><span style="color:#000000;">=</span><span style="color:#000000;"> </span><span style="color:#000000;">[</span><span style="color:#000000;">tableView dequeueReusableCellWithI<wbr>dentifier</wbr></span><span style="color:#000000;">:</span><span style="color:#2b91af;">CellIdentifier</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:#008b;">if</span><span style="color:#000000;"> </span><span style="color:#000000;">(</span><span style="color:#000000;">cell </span><span style="color:#000000;">==</span><span style="color:#000000;"> </span><span style="color:#008b;">nil</span><span style="color:#000000;">)</span><span style="color:#000000;"> </span><span style="color:#000000;">{</span><span style="color:#000000;"> cell </span><span style="color:#000000;">=</span><span style="color:#000000;"> </span><span style="color:#000000;">[[</span><span style="color:#2b91af;">UITableViewCell</span><span style="color:#000000;"> alloc</span><span style="color:#000000;">]</span><span style="color:#000000;"> initWithStyle</span><span style="color:#000000;">:</span><span style="color:#2b91af;">UITableViewCellStyleDefa<wbr>ult</wbr></span><span style="color:#000000;"> reuseIdentifier</span><span style="color:#000000;">:</span><span style="color:#2b91af;">CellIdentifier</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:#000000;">}</span><span style="color:#000000;"> </span><span style="color:#2b91af;">NSUInteger</span><span style="color:#000000;"> row </span><span style="color:#000000;">=</span><span style="color:#000000;"> </span><span style="color:#000000;">[</span><span style="color:#000000;">indexPath row</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:#2b91af;">NSDictionary</span><span style="color:#000000;"> </span><span style="color:#000000;">*</span><span style="color:#000000;">rowDict </span><span style="color:#000000;">=</span><span style="color:#000000;"> </span><span style="color:#000000;">[</span><span style="color:#008b;">self</span><span style="color:#000000;">.</span><span style="color:#000000;">listTeams objectAtIndex</span><span style="color:#000000;">:</span><span style="color:#000000;">row</span><span style="color:#000000;">];</span><span style="color:#000000;"> cell</span><span style="color:#000000;">.</span><span style="color:#000000;">textLabel</span><span style="color:#000000;">.</span><span style="color:#000000;">text </span><span style="color:#000000;">=</span><span style="color:#000000;"> </span><span style="color:#000000;">[</span><span style="color:#000000;">rowDict objectForKey</span><span style="color:#000000;">:@</span><span style="color:#8000;">"name"</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:#2b91af;">NSString</span><span style="color:#000000;"> </span><span style="color:#000000;">*</span><span style="color:#000000;">imagePath </span><span style="color:#000000;">=</span><span style="color:#000000;"> </span><span style="color:#000000;">[</span><span style="color:#000000;">rowDict objectForKey</span><span style="color:#000000;">:@</span><span style="color:#8000;">"image"</span><span style="color:#000000;">];</span><span style="color:#000000;"> imagePath </span><span style="color:#000000;">=</span><span style="color:#000000;"> </span><span style="color:#000000;">[</span><span style="color:#000000;">imagePath stringByAppendingString</span><span style="color:#000000;">:@</span><span style="color:#8000;">".png"</span><span style="color:#000000;">];</span><span style="color:#000000;"> cell</span><span style="color:#000000;">.</span><span style="color:#000000;">imageView</span><span style="color:#000000;">.</span><span style="color:#000000;">image </span><span style="color:#000000;">=</span><span style="color:#000000;"> </span><span style="color:#000000;">[</span><span style="color:#2b91af;">UIImage</span><span style="color:#000000;"> imageNamed</span><span style="color:#000000;">:</span><span style="color:#000000;">imagePath</span><span style="color:#000000;">];</span><span style="color:#000000;"> cell</span><span style="color:#000000;">.</span><span style="color:#000000;">accessoryType </span><span style="color:#000000;">=</span><span style="color:#000000;"> </span><span style="color:#2b91af;">UITableViewCellAccessory<wbr>DisclosureIndicator</wbr></span><span style="color:#000000;">;</span><span style="color:#000000;"> </span><span style="color:#008b;">return</span><span style="color:#000000;"> cell</span><span style="color:#000000;">;</span><span style="color:#000000;"> </span><span style="color:#000000;">}</span><span style="color:#000000;"> </span><span style="color:#000000;">-</span><span style="color:#000000;"> </span><span style="color:#000000;">(</span><span style="color:#008b;">void</span><span style="color:#000000;">)</span><span style="color:#000000;">tableView</span><span style="color:#000000;">:(</span><span style="color:#2b91af;">UITableView</span><span style="color:#000000;"> </span><span style="color:#000000;">*)</span><span style="color:#000000;">tableView didSelectRowAtIndexPath</span><span style="color:#000000;">:(</span><span style="color:#2b91af;">NSIndexPath</span><span style="color:#000000;"> </span><span style="color:#000000;">*)</span><span style="color:#000000;">indexPath </span><span style="color:#000000;">{</span><span style="color:#000000;"> </span><span style="color:#2b91af;">NSUInteger</span><span style="color:#000000;"> row </span><span style="color:#000000;">=</span><span style="color:#000000;"> </span><span style="color:#000000;">[</span><span style="color:#000000;">indexPath row</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:#2b91af;">NSDictionary</span><span style="color:#000000;"> </span><span style="color:#000000;">*</span><span style="color:#000000;">rowDict </span><span style="color:#000000;">=</span><span style="color:#000000;"> </span><span style="color:#000000;">[</span><span style="color:#008b;">self</span><span style="color:#000000;">.</span><span style="color:#000000;">listTeams objectAtIndex</span><span style="color:#000000;">:</span><span style="color:#000000;">row</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:#2b91af;">NSString</span><span style="color:#000000;"> </span><span style="color:#000000;">*</span><span style="color:#000000;">rowValue </span><span style="color:#000000;">=</span><span style="color:#000000;"> </span><span style="color:#000000;">[</span><span style="color:#000000;">rowDict objectForKey</span><span style="color:#000000;">:@</span><span style="color:#8000;">"name"</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:#2b91af;">NSString</span><span style="color:#000000;"> </span><span style="color:#000000;">*</span><span style="color:#000000;">message </span><span style="color:#000000;">=</span><span style="color:#000000;"> </span><span style="color:#000000;">[[</span><span style="color:#2b91af;">NSString</span><span style="color:#000000;"> alloc</span><span style="color:#000000;">]</span><span style="color:#000000;"> initWithFormat</span><span style="color:#000000;">:@”您选择了%@队。”,</span><span style="color:#000000;"> rowValue</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:#2b91af;">UIAlertView</span><span style="color:#000000;"> </span><span style="color:#000000;">*</span><span style="color:#000000;">alert </span><span style="color:#000000;">=</span><span style="color:#000000;"> </span><span style="color:#000000;">[[</span><span style="color:#2b91af;">UIAlertView</span><span style="color:#000000;"> alloc</span><span style="color:#000000;">]</span><span style="color:#000000;">initWithTitle</span><span style="color:#000000;">:@”请选择球队”</span><span style="color:#000000;"> message</span><span style="color:#000000;">:</span><span style="color:#000000;">message </span><span style="color:#008b;">delegate</span><span style="color:#000000;">:</span><span style="color:#008b;">self</span><span style="color:#000000;"> cancelButtonTitle</span><span style="color:#000000;">:@”</span><span style="color:#2b91af;">Ok</span><span style="color:#000000;">”</span><span style="color:#000000;"> otherButtonTitles</span><span style="color:#000000;">:</span><span style="color:#008b;">nil</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:#000000;">[</span><span style="color:#000000;">alert show</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:#000000;">[</span><span style="color:#000000;">tableView deselectRowAtIndexPath</span><span style="color:#000000;">:</span><span style="color:#000000;">indexPath animated</span><span style="color:#000000;">:</span><span style="color:#000000;">YES</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:#000000;">}</span></code>
大家看看上面的3个方法会有什么问题呢?如果代码是基于ARC的是没有问题的,遗憾的是基于MRR,上面的代码都存在内存泄漏的可能性。理论上讲内存泄漏是对象或变量没有释放引起的,但实践证明并非所有的未释放对象或变量都会导致内存泄漏,这与硬件环境和操作系统环境有关,因此我们需要检测工具帮助我们找到这些“泄漏点”。
在Xcode中提供了两种工具帮助查找泄漏点:Analyze和Profile,Analyze是静态分析工具可以通过菜单Product→Analyze启动,为静态分析之后的代码画面;Profile是动态分析工具,这个工具叫“Instruments”,它是Xcode集成在一起,可以在Xcode中通过菜单Product→Profile启动,Instruments有很多TraceTemplate(跟踪模板)可以动态分析和跟踪内存、CPU和文件系统。
我们可以两个工具结合使用查找泄漏点,先使用Analyze静态分析查找可疑泄漏点,再用Profile动态分析中的Leaks和Allocations跟踪模板进行动态跟踪分析,确认这些点是否泄漏,或者是否有新的泄漏出现等。
其中的线段表明了程序执行的路径,在这个路径中,1:说明在25行Objective-C对象引用计数是1,说明在这里创建了一个Objective-C对象;2:说明在27行引用计数为1这个,该对象没有释放,怀疑有泄漏。这样的说明已经很明显的告诉我们问题所在了,[[NSArray alloc] initWithContentsOfFile:plistPath]创建了一个对象,并赋值给listTeams属性所代表的成员变量,然而完成了赋值工作之后,创建的对象并没有显示地发送release和autorelease消息。代码修改
<code style="padding:0px; font-family:Menlo,Monaco,Consolas,'Courier new',monospace; color:inherit; background-color:transparent; border:0px"><span style="color:#2b91af;">NSArray</span><span style="color:#000000;"> </span><span style="color:#000000;">*</span><span style="color:#000000;">array </span><span style="color:#000000;">=</span><span style="color:#000000;"> </span><span style="color:#000000;">[[</span><span style="color:#2b91af;">NSArray</span><span style="color:#000000;"> alloc</span><span style="color:#000000;">]</span><span style="color:#000000;"> initWithContentsOfFile</span><span style="color:#000000;">:</span><span style="color:#000000;">plistPath</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:#008b;">self</span><span style="color:#000000;">.</span><span style="color:#000000;">listTeams </span><span style="color:#000000;">=</span><span style="color:#000000;"> array</span><span style="color:#000000;">;</span><span style="color:#000000;"> </span><span style="color:#000000;">[</span><span style="color:#000000;">array release</span><span style="color:#000000;">];</span></code>
我们看一下tableView:cellForRowAtIndexPath:方法中的疑似泄漏点行末尾的蓝色图标展开分析结果
其中主要是说明UITableViewCell*类型的cell对象在64行有可能存在泄漏。在表视图中tableView:cellForRowAtIndexPath:方法是为表视图单元格实例化并设置数据的,因此cell对象实例化后不能马上release,应该使用autorelease延迟释放。可以在创建cell对象的时候发送autorelease消息,代码修改如下:
<code style="padding:0px; font-family:Menlo,Monaco,Consolas,'Courier new',monospace; color:inherit; background-color:transparent; border:0px"><span style="color:#008b;">if</span><span style="color:#000000;"> </span><span style="color:#000000;">(</span><span style="color:#000000;">cell </span><span style="color:#000000;">==</span><span style="color:#000000;"> </span><span style="color:#008b;">nil</span><span style="color:#000000;">)</span><span style="color:#000000;"> </span><span style="color:#000000;">{</span><span style="color:#000000;"> cell </span><span style="color:#000000;">=</span><span style="color:#000000;"> </span><span style="color:#000000;">[[[</span><span style="color:#2b91af;">UITableViewCell</span><span style="color:#000000;"> alloc</span><span style="color:#000000;">]</span><span style="color:#000000;"> initWithStyle</span><span style="color:#000000;">:</span><span style="color:#2b91af;">UITableViewCellStyleDefa<wbr>ult</wbr></span><span style="color:#000000;"> reuseIdentifier</span><span style="color:#000000;">:</span><span style="color:#2b91af;">CellIdentifier</span><span style="color:#000000;">]</span><span style="color:#000000;"> autorelease</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:#000000;">}</span></code>
我们看一下tableView:didSelectRowAtIndexPath:方法中的疑似泄漏点有两个,行末尾的图标展开分析结果。
message对象创建之后没有释放,我们只需要在[alert show]之后添加[messagerelease]语句代码就可以了。在Objective-C中实例化对象有两种方式:
<code style="padding:0px; font-family:Menlo,Monaco,Consolas,'Courier new',monospace; color:inherit; background-color:transparent; border:0px"><span style="color:#2b91af;">NSString</span><span style="color:#000000;"> </span><span style="color:#000000;">*</span><span style="color:#000000;">message </span><span style="color:#000000;">=</span><span style="color:#000000;"> </span><span style="color:#000000;">[[</span><span style="color:#2b91af;">NSString</span><span style="color:#000000;"> alloc</span><span style="color:#000000;">]</span><span style="color:#000000;"> initWithFormat</span><span style="color:#000000;">:@”您选择了%@队。”,</span><span style="color:#000000;"> rowValue</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:#000000;">①</span><span style="color:#000000;"> </span><span style="color:#2b91af;">NSString</span><span style="color:#000000;"> </span><span style="color:#000000;">*</span><span style="color:#000000;">message </span><span style="color:#000000;">=</span><span style="color:#000000;"> </span><span style="color:#000000;">[</span><span style="color:#2b91af;">NSString</span><span style="color:#000000;"> stringWithFormat</span><span style="color:#000000;">:@</span><span style="color:#8000;">"您选择了%@队。"</span><span style="color:#000000;">,</span><span style="color:#000000;"> rowValue</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:#000000;">②</span></code>
①行所示以init-开头构造方法,它的是在alloc之后调用该方法我们称为“实例构造方法”,该方法创建对象所有权是调用者,调用者需要对它的生命周期负责,具体说负责创建和释放。而另一种是②行所示string-(去掉NS后类名)开头方法,它是通过类直接调用我们称为“类级构造方法”,该方法是创建的对象所有权非调用者所有,调用者不无权释放它,否则就会因过渡释放而“僵尸化”,这个问题我们会在下一节介绍。
UIAlertView*类型alert对象创建之后没有释放,我们只需要在[alert show]之后添加[alertrelease]语句代码就可以了,修改之后的代码
<code style="padding:0px; font-family:Menlo,Monaco,Consolas,'Courier new',monospace; color:inherit; background-color:transparent; border:0px"><span style="color:#000000;">-</span><span style="color:#000000;"> </span><span style="color:#000000;">(</span><span style="color:#008b;">void</span><span style="color:#000000;">)</span><span style="color:#000000;">tableView</span><span style="color:#000000;">:(</span><span style="color:#2b91af;">UITableView</span><span style="color:#000000;"> </span><span style="color:#000000;">*)</span><span style="color:#000000;">tableView didSelectRowAtIndexPath</span><span style="color:#000000;">:(</span><span style="color:#2b91af;">NSIndexPath</span><span style="color:#000000;"> </span><span style="color:#000000;">*)</span><span style="color:#000000;">indexPath </span><span style="color:#000000;">{</span><span style="color:#000000;"> </span><span style="color:#2b91af;">NSUInteger</span><span style="color:#000000;"> row </span><span style="color:#000000;">=</span><span style="color:#000000;"> </span><span style="color:#000000;">[</span><span style="color:#000000;">indexPath row</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:#2b91af;">NSDictionary</span><span style="color:#000000;"> </span><span style="color:#000000;">*</span><span style="color:#000000;">rowDict </span><span style="color:#000000;">=</span><span style="color:#000000;"> </span><span style="color:#000000;">[</span><span style="color:#008b;">self</span><span style="color:#000000;">.</span><span style="color:#000000;">listTeams objectAtIndex</span><span style="color:#000000;">:</span><span style="color:#000000;">row</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:#2b91af;">NSString</span><span style="color:#000000;"> </span><span style="color:#000000;">*</span><span style="color:#000000;">rowValue </span><span style="color:#000000;">=</span><span style="color:#000000;"> </span><span style="color:#000000;">[</span><span style="color:#000000;">rowDict objectForKey</span><span style="color:#000000;">:@</span><span style="color:#8000;">"name"</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:#2b91af;">NSString</span><span style="color:#000000;"> </span><span style="color:#000000;">*</span><span style="color:#000000;">message </span><span style="color:#000000;">=</span><span style="color:#000000;"> </span><span style="color:#000000;">[[</span><span style="color:#2b91af;">NSString</span><span style="color:#000000;"> alloc</span><span style="color:#000000;">]</span><span style="color:#000000;"> initWithFormat</span><span style="color:#000000;">:@”您选择了%@队。”,</span><span style="color:#000000;"> rowValue</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:#2b91af;">UIAlertView</span><span style="color:#000000;"> </span><span style="color:#000000;">*</span><span style="color:#000000;">alert </span><span style="color:#000000;">=</span><span style="color:#000000;"> </span><span style="color:#000000;">[[</span><span style="color:#2b91af;">UIAlertView</span><span style="color:#000000;"> alloc</span><span style="color:#000000;">]</span><span style="color:#000000;">initWithTitle</span><span style="color:#000000;">:@”请选择球队”</span><span style="color:#000000;"> message</span><span style="color:#000000;">:</span><span style="color:#000000;">message </span><span style="color:#008b;">delegate</span><span style="color:#000000;">:</span><span style="color:#008b;">self</span><span style="color:#000000;"> cancelButtonTitle</span><span style="color:#000000;">:@”</span><span style="color:#2b91af;">Ok</span><span style="color:#000000;">”</span><span style="color:#000000;"> otherButtonTitles</span><span style="color:#000000;">:</span><span style="color:#008b;">nil</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:#000000;">[</span><span style="color:#000000;">alert show</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:#000000;">[</span><span style="color:#000000;">alert release</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:#000000;">[</span><span style="color:#000000;">message release</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:#000000;">[</span><span style="color:#000000;">tableView deselectRowAtIndexPath</span><span style="color:#000000;">:</span><span style="color:#000000;">indexPath animated</span><span style="color:#000000;">:</span><span style="color:#000000;">YES</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:#000000;">}</span></code>
上面介绍的使用Analyze静态分析查找可疑泄漏点,之所以称为“可疑泄漏点”,但是这些点未必一定泄漏,确认这些点是否泄漏还要通过Profile动态分析工具Instruments中的Leaks和Allocations跟踪模板,Analyze静态分析只是一个理论上的预测过程。通过菜单Product→Profile启动, Profile动态分析工具中选择Leaks模板
Instruments中虽然是选择了Leaks模板,但默认情况也会添加Allocations模板,基本上凡是分析内存都会使用Allocations模板,它可以监控内存分布情况,选中Allocations模板(图中①区域),右边③区域会显示随着时间的变化内存使用折线图表,同时在④区域会显示内存使用的详细信息,其中刚刚对象分配情况。点击Leaks模板(图中②区域),可以查看内存泄漏情况,如果在③区域有红线出现,则有内存泄漏,④区域会显示泄漏的对象。
出现的泄漏是在点击表视图中单元格测试tableView:didSelectRowAtIndexPath:方法方法时候发生的,其中NSCFString类型的对象发生了泄漏,NSCFString类型在NSFoundation中是NSString*类型。点击泄漏对象前面的三角形展开对象,可以看到它们的内存地址、占用字节、所属框架和响应方法信息。
打开扩展详细视图,可以看到右边的跟踪堆栈信息,其中我们自己应用代码,可以点击进入我们程序代码,会打开对应代码。
代码77并不是泄漏点,而是其中的NSString*类型对象在之后发生了泄漏,因此可以断定是message对象之后没有释放导致泄漏。我们修改代码如下:
<code style="padding:0px; font-family:Menlo,Monaco,Consolas,'Courier new',monospace; color:inherit; background-color:transparent; border:0px"><span style="color:#000000;">-</span><span style="color:#000000;"> </span><span style="color:#000000;">(</span><span style="color:#008b;">void</span><span style="color:#000000;">)</span><span style="color:#000000;">tableView</span><span style="color:#000000;">:(</span><span style="color:#2b91af;">UITableView</span><span style="color:#000000;"> </span><span style="color:#000000;">*)</span><span style="color:#000000;">tableView didSelectRowAtIndexPath</span><span style="color:#000000;">:(</span><span style="color:#2b91af;">NSIndexPath</span><span style="color:#000000;"> </span><span style="color:#000000;">*)</span><span style="color:#000000;">indexPath </span><span style="color:#000000;">{</span><span style="color:#000000;"> </span><span style="color:#2b91af;">NSUInteger</span><span style="color:#000000;"> row </span><span style="color:#000000;">=</span><span style="color:#000000;"> </span><span style="color:#000000;">[</span><span style="color:#000000;">indexPath row</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:#2b91af;">NSDictionary</span><span style="color:#000000;"> </span><span style="color:#000000;">*</span><span style="color:#000000;">rowDict </span><span style="color:#000000;">=</span><span style="color:#000000;"> </span><span style="color:#000000;">[</span><span style="color:#008b;">self</span><span style="color:#000000;">.</span><span style="color:#000000;">listTeams objectAtIndex</span><span style="color:#000000;">:</span><span style="color:#000000;">row</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:#2b91af;">NSString</span><span style="color:#000000;"> </span><span style="color:#000000;">*</span><span style="color:#000000;">rowValue </span><span style="color:#000000;">=</span><span style="color:#000000;"> </span><span style="color:#000000;">[</span><span style="color:#000000;">rowDict objectForKey</span><span style="color:#000000;">:@</span><span style="color:#8000;">"name"</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:#2b91af;">NSString</span><span style="color:#000000;"> </span><span style="color:#000000;">*</span><span style="color:#000000;">message </span><span style="color:#000000;">=</span><span style="color:#000000;"> </span><span style="color:#000000;">[[</span><span style="color:#2b91af;">NSString</span><span style="color:#000000;"> alloc</span><span style="color:#000000;">]</span><span style="color:#000000;"> initWithFormat</span><span style="color:#000000;">:@”您选择了%@队。”,</span><span style="color:#000000;"> rowValue</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:#2b91af;">UIAlertView</span><span style="color:#000000;"> </span><span style="color:#000000;">*</span><span style="color:#000000;">alert </span><span style="color:#000000;">=</span><span style="color:#000000;"> </span><span style="color:#000000;">[[</span><span style="color:#2b91af;">UIAlertView</span><span style="color:#000000;"> alloc</span><span style="color:#000000;">]</span><span style="color:#000000;">initWithTitle</span><span style="color:#000000;">:@”请选择球队”</span><span style="color:#000000;"> message</span><span style="color:#000000;">:</span><span style="color:#000000;">message </span><span style="color:#008b;">delegate</span><span style="color:#000000;">:</span><span style="color:#008b;">self</span><span style="color:#000000;"> cancelButtonTitle</span><span style="color:#000000;">:@”</span><span style="color:#2b91af;">Ok</span><span style="color:#000000;">”</span><span style="color:#000000;"> otherButtonTitles</span><span style="color:#000000;">:</span><span style="color:#008b;">nil</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:#000000;">[</span><span style="color:#000000;">alert show</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:#000000;">[</span><span style="color:#000000;">message release</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:#000000;">[</span><span style="color:#000000;">tableView deselectRowAtIndexPath</span><span style="color:#000000;">:</span><span style="color:#000000;">indexPath animated</span><span style="color:#000000;">:</span><span style="color:#000000;">YES</span><span style="color:#000000;">];</span><span style="color:#000000;"> </span><span style="color:#000000;">}</span></code>
添加[messagerelease]语句。很多人还会猜测alert对象(UIAlertView*)会有泄漏,因此重新运行Instruments工具,反复点击单元格测试,并未发现表示内存泄漏的红线!Instruments工具认为alert对象不释放不会引起内存泄漏,如果我们想进一步评估它对于内存的应用,这个时候我们可以看看Allocations模板的折线图表,每次点击总占用内存数都有所增加,这说明alert对象没有释放虽然不是很严重,但是也会增加占用内存,因此alert对象释放也是必须的。
这就是我们介绍的内存泄漏问题解决方法,事实上内存泄漏是极其复杂问题,工具使用是一方面,经验是另一方面。提高经验,然后借助于工具才是解决内存泄漏的根本。