Утечка памяти в NSMutableArray при перезагрузке объектов
Я использую контроллер Three20/TTThumbsview для загрузки фотографий. Я уже довольно давно пытаюсь исправить утечку памяти при настройке фотоисточника. Я новичок в управлении объектами 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), но не могу понять, как исправить утечку памяти. Любое предложение действительно ценится.
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 и release после addObject). Вы должны удалить один из них.
Кроме того, я бы также порекомендовал вам несколько вещей:
- Переключитесь на ARC: Если вы новичок, вы обязательно воспользуетесь этим, потому что вам больше не придется беспокоиться практически об управлении памятью. Ваш код будет свободен от утечек объектов и сбоев памяти. Просто будьте осторожны с циклами сохранения памяти и с переменным владельцем, и все готово.
Удалить
NSMutableArray *_photoList;
, вы не используете его, так как вы объявляете свой syntetizer свойства как@synthesize photoList;
, Кроме того, как прокомментировал Роберт ниже, вы должны использовать эту форму, чтобы четко различать, когда вы используете свойство:@synthesize photoList = photoList_;
Подчеркивание лучше в конце, потому что Apple использует его в начале, и вы не хотите случайно использовать одно и то же имя переменной.Используйте средства доступа к собственности для управления ivars. Вместо этого:
if (photoList == nil) photoList = [[NSMutableArray alloc] init ];
попробуй это:
if (photoList == nil)
self.photoList = [[[NSMutableArray alloc] init] autorelease];
В вашем примере обе строки имеют одинаковое поведение, но вторая предпочтительнее, потому что она будет полагаться на вашу переменную политику памяти. Если вы измените свое хранилище на копию или назначение однажды, ваш код все равно будет работать без проблем. Таким же образом, освободите свою память, назначив свой ivar ноль, как это self.photoList = nil
Почему вы думаете, что у вас есть утечка предмета?
Напомню мои наблюдения:
Я не думаю, что это связано, но я думаю, что вы хотите
@synthesize photoList = _photoList
, Прямо сейчас у вас есть два ивара, тот, который вы объявили явно,_photoList
и тот, который создается неявно из вашего свойства photoList,photoList
, Я также избавился бы от существующего явного объявления ивара _photoList, так как нынешний Apple рекомендует, чтобы вы позволили оператору синтеза сделать это за вас. (И это предотвращает такие ситуации, когда вы случайно оказались с дополнительными иварами.)Кроме того, опять-таки не связано с утечкой (а скорее обратная проблема), но jphoto слишком много, потому что вы выпускаете оба
autorelease
также как иrelease
, Последнее достаточно.Кроме того, я не вижу твоего
sqlite3_finalize()
а такжеsqlite3_close()
заявления. Где они? Согласно документам, "каждое подготовленное утверждение должно быть уничтожено с помощью вызова [sqlite3_finalize()
] во избежание утечек памяти."Наконец, если у вас все еще есть утечка после добавления операторов finalize/close для вашей базы данных, я бы предложил выполнить ваш код через инструмент утечек в профилировщике, чтобы найти утечку. Это поможет вам точно определить, что происходит.