1

我正在使用 Three20/TTThumbsviewcontroller 加载照片。很长一段时间以来,我一直在努力解决设置光源时的内存泄漏问题。我是对象 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 方法时会发生内存泄漏......我觉得声明 NSMutableArray (photoList) 有问题,但无法弄清楚如何修复内存泄漏。任何建议都非常感谢。

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:如果您是新手,您肯定会利用它,因为您不必再​​担心几乎任何内存管理。您的代码将没有泄漏的对象和内存崩溃。只需注意内存保留周期和可变所有权,您就完成了。
  2. 删除NSMutableArray *_photoList;,您没有使用它,因为您将属性 syntetizer 声明为@synthesize photoList;. 另外,正如罗伯特在下面评论的那样,您应该在使用属性时使用这种形式来清楚地区分: @synthesize photoList = photoList_; 下划线最好放在最后,因为Apple在开头使用它并且您不想意外使用相同的变量名。

  3. 使用属性访问器来管理 ivars。而不是这样做:

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

试试这个:

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

在您的示例中,两行具有相同的行为,但第二行更可取,因为它将依赖于您的可变内存策略。如果您有一天通过副本或分配更改保留,您的代码仍然可以正常工作。以同样的方式,通过像这样为您的 ivar 分配一个 nil 来释放您的内存self.photoList = nil

为什么你认为你有一个泄漏的对象?

于 2012-06-17T15:51:24.953 回答
0

回顾一下我的观察:

  1. 我不认为这是相关的,但我认为你想要@synthesize photoList = _photoList. 现在,您有两个 ivars,一个是您显式声明的,_photoList一个是从您的 photoList 属性隐式创建的photoList。我还将摆脱 _photoList ivar 的现有显式声明,因为 Apple 当前建议您让 synthesize 语句为您执行此操作。(并且它可以防止出现这种情况,在这种情况下你意外地得到了额外的 ivars。)

  2. 此外,再次与泄漏无关(而是相反的问题),但 jphoto 被过度释放,因为您同时发布了 anautoreleaserelease. 后者就足够了。

  3. 另外,我没有看到您的sqlite3_finalize()sqlite3_close()声明。他们在哪?根据文档,“必须使用对 [] 的调用来销毁每个准备好的语句sqlite3_finalize(),以避免内存泄漏。”

  4. 最后,如果在为数据库添加 finalize/close 语句后仍然存在泄漏,我建议通过分析器中的泄漏工具运行代码以查找泄漏。这将帮助您准确识别泄漏的内容。

于 2012-06-17T16:15:30.433 回答