1

写真の読み込みに Three20/TTThumbsviewcontroller を使用しています。photosource を設定する際のメモリリークを修正するために、かなり長い間苦労しています。私はオブジェクト C と iOS メモリ管理の初心者です。次のコードを見て、明らかな間違いや変数の宣言と解放のエラーを指摘してください。

-- PhotoViewController.h

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

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

-- 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];

}

上記の LoadPhotosource メソッドを別のクエリで再度呼び出すと、メモリ リークが発生します。どんな提案でも大歓迎です。

4

2 に答える 2

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];

よく見ると、二重のリリースがあります (alloc での自動リリースと、addObject の後のリリース)。それらのいずれかを削除する必要があります。

それに加えて、いくつかのこともお勧めします。

  1. ARC への切り替え: 初心者の場合は、メモリ管理についてほとんど心配する必要がないため、ARC を確実に活用できます。コードからオブジェクトのリークやメモリ クラッシュがなくなります。メモリ保持サイクルと変数の所有権に注意してください。
  2. を削除NSMutableArray *_photoList;します。プロパティ シンテタイザーを として宣言しているため、使用していません@synthesize photoList;。さらに、Robert が以下にコメントしたように、プロパティを使用する場合は、この形式を使用して明確に区別する必要があります @synthesize photoList = photoList_; 。Apple が最初にアンダースコアを使用し、誤って同じ変数名を使用したくないため、最後にアンダースコアを使用することをお勧めします。

  3. プロパティ アクセサーを使用して ivar を管理します。これを行う代わりに:

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

これを試して:

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

あなたの例では、両方の行が同じ動作をしていますが、可変メモリ ポリシーに依存するため、2 番目の行が望ましいです。ある日、コピーまたは割り当てによって保持を変更しても、コードは問題なく動作します。同様に、次のようにivarにnilを割り当ててメモリを解放しますself.photoList = nil

なぜあなたはリークされたオブジェクトを持っていると思いますか?

于 2012-06-17T15:51:24.953 に答える
0

私の観察を要約すると:

  1. 関係ないと思いますが、欲しいと思います@synthesize photoList = _photoList。現在、2つのivarがあります。1つは明示的に宣言したもので、もう_photoList1つはphotoListプロパティから暗黙的に作成されたものですphotoList。また、Appleの現在のアドバイスでは、syntheticステートメントにそれを行わせるようにアドバイスされているため、_photoListivarの既存の明示的な宣言を削除します。(そして、それはあなたが偶然に追加のivarsになってしまったこのような状況を防ぎます。)

  2. また、これもリークとは関係ありませんが(逆の問題です)、とを発行しているため、jphotoは過剰にリリースされていautoreleaseますrelease。後者で十分です。

  3. sqlite3_finalize()また、私はあなたとsqlite3_close()声明を見ていません。彼らはどこにいる?ドキュメントによると、「sqlite3_finalize()メモリリークを回避するには、[]の呼び出しを使用してすべてのプリペアドステートメントを破棄する必要があります。」

  4. 最後に、データベースのfinalize / closeステートメントを追加した後もリークが発生する場合は、プロファイラーのリークツールを使用してコードを実行し、リークを見つけることをお勧めします。これは、何が漏れているかを正確に特定するのに役立ちます。

于 2012-06-17T16:15:30.433 に答える