iOS開發那些事--性能優化–內存泄露問題的解決(轉)

內存泄漏問題的解決

內存泄漏(Memory Leaks)是當一個對象或變量在使用完成後沒有釋放掉,這個對象一直佔有着這塊內存,直到應用中止。若是這種對象過多內存就會耗盡,其它的應用就沒法運行。這個問題在C++、C和Objective-C的MRR中是比較廣泛的問題。javascript

在Objective-C中釋放對象的內存是發送release和autorelease消息,它們都是能夠將引用計數減1,當爲引用計數爲0時候,release消息會使對象馬上釋放,autorelease消息會使對象放入內存釋放池中延遲釋放。java

上代碼:ios

Java代碼    收藏代碼
  1. - (void)viewDidLoad  
  2.   
  3. {  
  4.   
  5. [super viewDidLoad];  
  6.   
  7. NSBundle *bundle = [NSBundle mainBundle];  
  8.   
  9. NSString *plistPath = [bundle pathForResource:@"team"  
  10.   
  11. ofType:@"plist"];  
  12.   
  13. //獲取屬性列表文件中的所有數據  
  14.   
  15. self.listTeams = [[NSArray alloc] initWithContentsOfFile:plistPath];  
  16.   
  17. }  
  18.   
  19. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath  
  20.   
  21. {  
  22.   
  23. static NSString *CellIdentifier = @」CellIdentifier」;  
  24.   
  25. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];  
  26.   
  27. if (cell == nil) {  
  28.   
  29. cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];  
  30.   
  31. }  
  32.   
  33. NSUInteger row = [indexPath row];  
  34.   
  35. NSDictionary *rowDict = [self.listTeams objectAtIndex:row];  
  36.   
  37. cell.textLabel.text =  [rowDict objectForKey:@"name"];  
  38.   
  39. NSString *imagePath = [rowDict objectForKey:@"image"];  
  40.   
  41. imagePath = [imagePath stringByAppendingString:@".png"];  
  42.   
  43. cell.imageView.image = [UIImage imageNamed:imagePath];  
  44.   
  45. cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;  
  46.   
  47. return cell;  
  48.   
  49. }  
  50.   
  51. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath  
  52.   
  53. {  
  54.   
  55. NSUInteger row = [indexPath row];  
  56.   
  57. NSDictionary *rowDict = [self.listTeams objectAtIndex:row];  
  58.   
  59. NSString *rowValue  =  [rowDict objectForKey:@"name"];  
  60.   
  61. NSString *message = [[NSString alloc] initWithFormat:@」您選擇了%@隊。」, rowValue];  
  62.   
  63. UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@」請選擇球隊」  
  64.   
  65. message:message  
  66.   
  67. delegate:self  
  68.   
  69. cancelButtonTitle:@」Ok」  
  70.   
  71. otherButtonTitles:nil];  
  72.   
  73. [alert show];  
  74.   
  75. [tableView deselectRowAtIndexPath:indexPath animated:YES];  
  76.   
  77. }  

 

大 家看看上面的3個方法會有什麼問題呢?若是代碼是基於ARC的是沒有問題的,遺憾的是基於MRR,上面的代碼都存在內存泄漏的可能性。理論上講內 存泄漏是對象或變量沒有釋放引發的,但實踐證實並不是全部的未釋放對象或變量都會致使內存泄漏,這與硬件環境和操做系統環境有關,所以咱們須要檢測工具幫助 咱們找到這些「泄漏點」。app

在Xcode中提供了兩種工具幫助查找泄漏點:Analyze和Profile,Analyze是靜態分析工具 能夠經過菜單 Product→Analyze啓動,爲靜態分析以後的代碼畫面;Profile是動態分析工具,這個工具叫「Instruments」,它是Xcode 集成在一塊兒,能夠在Xcode中經過菜單Product→Profile啓動,Instruments有不少Trace Template(跟蹤模板)能夠動態分析和跟蹤內存、CPU和文件系統。框架

12

咱們能夠兩個工具結合使用查找泄漏點,先使用Analyze靜態分析查找可疑泄漏點,再用Profile動態分析中的Leaks和Allocations跟蹤模板進行動態跟蹤分析,確認這些點是否泄漏,或者是否有新的泄漏出現等。工具

其 中的線段代表了程序執行的路徑,在這個路徑中,1:說明在25行Objective-C對象引用計數是1,說明在這裏建立了一個 Objective-C對象;2:說明在27行引用計數爲1這個,該對象沒有釋放,懷疑有泄漏。這樣的說明已經很明顯的告訴咱們問題所在了, [[NSArray alloc] initWithContentsOfFile:plistPath]建立了一個對象,並賦值給 listTeams屬性所表明的成員變量,然而完成了賦值工做以後,建立的對象並無顯示地發送release和autorelease消息。代碼修改測試

NSArray *array = [[NSArray alloc] initWithContentsOfFile:plistPath];spa

self.listTeams = array;操作系統

[array release];code

咱們看一下tableView:cellForRowAtIndexPath:方法中的疑似泄漏點行末尾的藍色圖標展開分析結果

3

其 中主要是說明UITableViewCell*類型的cell對象在64行有可能存在泄漏。在表視圖中 tableView:cellForRowAtIndexPath:方法是爲表視圖單元格實例化並設置數據的,所以cell對象實例化後不能立刻 release,應該使用autorelease延遲釋放。能夠在建立cell對象的時候發送autorelease消息,代碼修改以下:

if (cell == nil) {

cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];

}

咱們看一下tableView:didSelectRowAtIndexPath:方法中的疑似泄漏點有兩個,行末尾的圖標展開分析結果。

 4

 message對象建立以後沒有釋放,咱們只須要在[alert show]以後添加[message release]語句代碼就能夠了。在Objective-C中實例化對象有兩種方式:

NSString *message = [[NSString alloc] initWithFormat:@」您選擇了%@隊。」, rowValue];                             ①

NSString *message = [NSString stringWithFormat:@"您選擇了%@隊。", rowValue];                           ②

① 行所示以init-開頭構造方法,它的是在alloc以後調用該方法咱們稱爲「實例構造方法」,該方法建立對象全部權是調用者,調用者須要對它的 生命週期負責,具體說負責建立和釋放。而另外一種是②行所示string-(去掉NS後類名)開頭方法,它是經過類直接調用咱們稱爲「類級構造方法」,該方 法是建立的對象全部權非調用者全部,調用者不無權釋放它,不然就會因過渡釋放而「殭屍化」,這個問題咱們會在下一節介紹。

UIAlertView*類型alert對象建立以後沒有釋放,咱們只須要在[alert show]以後添加[alert release]語句代碼就能夠了,修改以後的代碼

Java代碼    收藏代碼
  1. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath  
  2.   
  3. {  
  4.   
  5. NSUInteger row = [indexPath row];  
  6.   
  7. NSDictionary *rowDict = [self.listTeams objectAtIndex:row];  
  8.   
  9. NSString *rowValue  =  [rowDict objectForKey:@"name"];  
  10.   
  11. NSString *message = [[NSString alloc] initWithFormat:@」您選擇了%@隊。」, rowValue];  
  12.   
  13. UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@」請選擇球隊」  
  14.   
  15. message:message  
  16.   
  17. delegate:self  
  18.   
  19. cancelButtonTitle:@」Ok」  
  20.   
  21. otherButtonTitles:nil];  
  22.   
  23. [alert show];  
  24.   
  25. [alert release];  
  26.   
  27. [message release];  
  28.   
  29. [tableView deselectRowAtIndexPath:indexPath animated:YES];  
  30.   
  31. }  

 

上 面介紹的使用Analyze靜態分析查找可疑泄漏點,之因此稱爲「可疑泄漏點」,可是這些點未必必定泄漏,確認這些點是否泄漏還要經過 Profile動態分析工具Instruments中的Leaks和Allocations跟蹤模板,Analyze靜態分析只是一個理論上的預測過程。 經過菜單Product→Profile啓動, Profile動態分析工具中選擇Leaks模板

5

Instruments 中雖然是選擇了Leaks模板,但默認狀況也會添加Allocations模板,基本上凡是分析內存都會使用 Allocations模板,它能夠監控內存分佈狀況,選中Allocations模板(圖中①區域),右邊③區域會顯示隨着時間的變化內存使用折線圖 表,同時在④區域會顯示內存使用的詳細信息,其中剛剛對象分配狀況。點擊Leaks模板(圖中②區域),能夠查看內存泄漏狀況,若是在③區域有紅線出現, 則有內存泄漏,④區域會顯示泄漏的對象。

6

出 現的泄漏是在點擊表視圖中單元格測試tableView:didSelectRowAtIndexPath:方法方法時候發生的,其中 NSCFString類型的對象發生了泄漏,NSCFString類型在NSFoundation中是NSString*類型。點擊泄漏對象前面的三角形 展開對象,能夠看到它們的內存地址、佔用字節、所屬框架和響應方法信息。

7 打開擴展詳細視圖,能夠看到右邊的跟蹤堆棧信息,其中咱們本身應用代碼,能夠點擊進入咱們程序代碼,會打開對應代碼。8

代碼77並非泄漏點,而是其中的NSString*類型對象在以後發生了泄漏,所以能夠判定是message對象以後沒有釋放致使泄漏。咱們修改代碼以下:

Java代碼    收藏代碼
  1. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath  
  2.   
  3. {  
  4.   
  5. NSUInteger row = [indexPath row];  
  6.   
  7. NSDictionary *rowDict = [self.listTeams objectAtIndex:row];  
  8.   
  9. NSString *rowValue  =  [rowDict objectForKey:@"name"];  
  10.   
  11. NSString *message = [[NSString alloc] initWithFormat:@」您選擇了%@隊。」, rowValue];  
  12.   
  13. UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@」請選擇球隊」  
  14.   
  15. message:message  
  16.   
  17. delegate:self  
  18.   
  19. cancelButtonTitle:@」Ok」  
  20.   
  21. otherButtonTitles:nil];  
  22.   
  23. [alert show];  
  24.   
  25.     [message release];  
  26.   
  27. [tableView deselectRowAtIndexPath:indexPath animated:YES];  
  28.   
  29. }  

 

添 加[message release]語句。不少人還會猜想alert對象(UIAlertView*)會有泄漏,所以從新運行Instruments工具,反覆點擊單元格測 試,並未發現表示內存泄漏的紅線! Instruments工具認爲alert對象不釋放不會引發內存泄漏,若是咱們想進一步評估它對於內存的應用,這個時候咱們能夠看看 Allocations模板的折線圖表,每次點擊總佔用內存數都有所增長,這說明alert對象沒有釋放雖然不是很嚴重,可是也會增長佔用內存,所以 alert對象釋放也是必須的。

9

這就是咱們介紹的內存泄漏問題解決方法,事實上內存泄漏是極其複雜問題,工具使用是一方面,經驗是另外一方面。提升經驗,而後藉助於工具纔是解決內存泄漏的根本。

 

轉自:http://guandongsheng.iteye.com/blog/1782699

相關文章
相關標籤/搜索