2012-06-17 99 views
1

我正在使用Three20/TTThumbsviewcontroller加載照片。我很苦惱,因爲現在有一段時間來修復設置光源的內存泄漏。我是Object C & iOS內存管理的初學者。請看下面的代碼,並建議在聲明和釋放變量時出現任何明顯的錯誤或任何錯誤。重新加載對象時出現NSMutableArray內存泄漏

- PhotoViewController.h

@interface PhotoViewController : TTThumbsViewController <UIPopoverControllerDelegate,CategoryPickerDelegate,FilterPickerDelegate,UISearchBarDelegate>{ 
...... 
NSMutableArray *_photoList; 

...... 
@property(nonatomic,retain) NSMutableArray *photoList; 

- 上面LoadPhotosource方法與不同的查詢再次致電時PhotoViewController.m

@implementation PhotoViewController 
.... 

@synthesize photoList; 
..... 

- (void)LoadPhotoSource:(NSString *)query:(NSString *)title:(NSString*)stoneName{ 


NSLog(@"log- in loadPhotosource method"); 


if (photoList == nil) 
    photoList = [[NSMutableArray alloc] init ]; 

[photoList removeAllObjects]; 



@try { 
    sqlite3 *db; 
    NSFileManager *fileMgr = [NSFileManager defaultManager]; 
    NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; 

    NSString *dbPath = [documentsPath stringByAppendingPathComponent: @"DB.s3db"]; 
    BOOL success = [fileMgr fileExistsAtPath:dbPath]; 
    if(!success) 
    { 
     NSLog(@"Cannot locate database file '%@'.", dbPath); 
    } 

    if(!(sqlite3_open([dbPath UTF8String], &db) == SQLITE_OK)) 
    { 
     NSLog(@"An error has occured."); 
    } 


    NSString *_sql = query; 
    const char *sql = [_sql UTF8String]; 
    sqlite3_stmt *sqlStatement; 
    if(sqlite3_prepare(db, sql, -1, &sqlStatement, NULL) != SQLITE_OK) 
    { 
     NSLog(@"Problem with prepare statement"); 
    } 

    if ([stoneName length] != 0) 
    { 
     NSString *wildcardSearch = [NSString stringWithFormat:@"%@%%",[stoneName stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]]; 

     sqlite3_bind_text(sqlStatement, 1, [wildcardSearch UTF8String], -1, SQLITE_STATIC); 
    } 


    while (sqlite3_step(sqlStatement)==SQLITE_ROW) { 

     NSString* urlSmallImage = @"Mahallati_NoImage.png"; 
     NSString* urlThumbImage = @"Mahallati_NoImage.png"; 


     NSString *designNo = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement,2)]; 
     designNo = [designNo stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 

     NSString *desc = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement,7)]; 
     desc = [desc stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 

     NSString *caption = designNo;//[designNo stringByAppendingString:desc]; 
     caption = [caption stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 



     NSString *smallFilePath = [documentsPath stringByAppendingPathComponent: [NSString stringWithFormat:@"Small%@.JPG",designNo] ]; 
     smallFilePath = [smallFilePath stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 
     if ([fileMgr fileExistsAtPath:smallFilePath]){ 
      urlSmallImage = [NSString stringWithFormat:@"Small%@.JPG",designNo]; 
     } 

     NSString *thumbFilePath = [documentsPath stringByAppendingPathComponent: [NSString stringWithFormat:@"Thumb%@.JPG",designNo] ]; 
     thumbFilePath = [thumbFilePath stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 

     if ([fileMgr fileExistsAtPath:thumbFilePath]){ 
      urlThumbImage = [NSString stringWithFormat:@"Thumb%@.JPG",designNo]; 
     } 




     NSNumber *photoProductId = [NSNumber numberWithInt:(int)sqlite3_column_int(sqlStatement, 0)]; 
     NSNumber *photoPrice = [NSNumber numberWithInt:(int)sqlite3_column_int(sqlStatement, 6)]; 

     char *productNo1 = sqlite3_column_text(sqlStatement, 3); 
     NSString* productNo; 
     if (productNo1 == NULL) 
      productNo = nil; 
     else 
      productNo = [NSString stringWithUTF8String:productNo1]; 

     Photo *jphoto = [[[Photo alloc] initWithCaption:caption 
               urlLarge:[NSString stringWithFormat:@"documents://%@",urlSmallImage] 
               urlSmall:[NSString stringWithFormat:@"documents://%@",urlSmallImage] 
               urlThumb:[NSString stringWithFormat:@"documents://%@",urlThumbImage] 
                size:CGSizeMake(123, 123) 
               productId:photoProductId 
                price:photoPrice 
              description:desc 
               designNo:designNo 
               productNo:productNo 
          ] autorelease]; 



     [photoList addObject:jphoto]; 
     [jphoto release]; 



    } 


} 
@catch (NSException *exception) { 
    NSLog(@"An exception occured: %@", [exception reason]); 
} 



self.photoSource = [[[MockPhotoSource alloc] 
        initWithType:MockPhotoSourceNormal 
        title:[NSString stringWithFormat: @"%@",title] 
        photos: photoList 
        photos2:nil] autorelease]; 

} 

內存泄漏發生... 我覺得它的一些錯誤聲明NSMutableArray(photoList),但無法弄清楚如何修復內存泄漏。 任何建議真的很感激。

+0

我不認爲它是相關的,但我認爲你想'@synthesize photoList = _photoList'。現在,你有兩個ivars,一個是你明確聲明的'_photoList',另一個是你的photoList屬性'photoList'隱式創建的。 – Rob

+0

另外,再次無關,但我不明白jphoto是如何未被釋放的,因爲你同時發佈'autorelease'和'release'。 – Rob

+0

你是否已經通過探查器中的泄漏工具來運行它?這會告訴你哪個對象正在泄漏,這使得這個診斷更容易。 – Rob

回答

2

我還沒有看到你的代碼中的任何泄漏的對象,但實際上是一個雙重釋放的對象,最終將崩潰您的應用程序:

在你的最後幾行,你有這樣的:

Photo *jphoto = [[[Photo alloc] initWithCaption:caption 
               urlLarge:[NSString stringWithFormat:@"documents://%@",urlSmallImage] 
               urlSmall:[NSString stringWithFormat:@"documents://%@",urlSmallImage] 
               urlThumb:[NSString stringWithFormat:@"documents://%@",urlThumbImage] 
                size:CGSizeMake(123, 123) 
               productId:photoProductId 
                price:photoPrice 
               description:desc 
          designNo:designNo 
               productNo:productNo 
          ] autorelease]; 



     [photoList addObject:jphoto]; 
     [jphoto release]; 

如果你拿仔細一看,你有一個雙發佈(autorelease在alloc和release之後的addObject)。你應該刪除其中的一個。

除此之外,我還建議你幾件事情:

  1. 切換到ARC:如果你是新的,你會definetly利用它,因爲你不會擔心幾乎在任何內存管理了。您的代碼將免於泄漏的對象和內存崩潰。只要注意內存保留週期和變量所有權,就完成了。
  2. 刪除NSMutableArray *_photoList;,你沒有使用它,因爲你宣佈你的房產syntetizer爲@synthesize photoList;。另外,正如Robert在下面評論的那樣,您應該使用此表格清楚地區分您使用該屬性時的情況: @synthesize photoList = photoList_; 下劃線最終效果更好,因爲Apple在開始時使用它,並且不想意外使用相同的變量名稱。

  3. 使用屬性訪問器來管理ivars。相反,這樣做的:

    if (photoList == nil) photoList = [[NSMutableArray alloc] init ];

試試這個:

if (photoList == nil) 
    self.photoList = [[[NSMutableArray alloc] init] autorelease]; 

在您的例子兩條線都有相同的行爲,但第二個是可取的,因爲它會依賴於您的變量內存策略。如果您在一天內通過副本或分配更改您的保留,您的代碼仍然可以毫無問題地工作。以同樣的方式,釋放你的記憶,通過分配你的伊娃爾這樣一個零self.photoList = nil

你爲什麼認爲你有泄漏的對象?

+1

除了你去除_photoList顯式聲明的建議,我認爲他應該改變自己的綜合聲明'@synthesize photoList = _photoList',讓您可以隨時清楚時,他的直接訪問伊娃,當他使用酒店的干將和制定者。 – Rob

+0

Angel&Robert。非常感謝你們,你們真棒。我遵循了你的兩個建議,並且LoadPhotoSource方法中沒有內存泄漏。我沒有意識到jPhoto應該只有一個發行版或autorelease。我也刪除了NSMutableArray * _photoList。感謝您的詳細解釋和建議。 – Davin

+0

歡迎您。然後,也許你沒有要求內存泄漏,這是內存沒有在你的代碼中釋放,但由於過度釋放的對象的內存崩潰。 –

0

只是爲了回顧一下我的觀察:

  1. 我不認爲這是相關的,但我想你想@synthesize photoList = _photoList。現在,您有兩個ivars,您明確聲明的那個,_photoList,以及一個從您的photoList屬性photoList隱式創建的ivars。我也擺脫了_photoList ivar的現有明確聲明,因爲Apple當前建議您讓綜合語句爲您做這件事。 (它可以防止這種情況發生,最終導致額外的ivars意外終止。)

  2. 此外,再次與泄漏無關(但是相反的問題),但jphoto正在發佈,因爲您發佈既有autorelease也有release。後者是足夠的。

  3. 另外,我沒有看到你的sqlite3_finalize()sqlite3_close()聲明。他們在哪?根據docs,「爲了避免內存泄漏,必須使用致電[sqlite3_finalize()]銷燬每個準備好的聲明。」

  4. 最後,如果在爲數據庫添加完成/關閉語句後仍然有泄漏,我建議通過探查器中的泄漏工具將代碼運行到find the leak。這將幫助您準確識別泄漏情況。

+0

非常感謝。你是對的。它修復了內存泄漏問題,我也將關閉連接。 – Davin