我正在使用Three20 / TTThumbsviewcontroller来加载照片。我现在很长一段时间都在努力解决设置光源时的内存泄漏问题。我是Object 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)时出了点问题,但无法弄清楚如何修复内存泄漏。 任何建议都非常感谢。
答案 0 :(得分: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];
如果你仔细看看,你有一个双重释放(在addObject之后的alloc和release中自动释放)。你应该删除其中一个。
除此之外,我还建议你做一些事情:
删除NSMutableArray *_photoList;
,因为您将属性syntetizer声明为@synthesize photoList;
,所以您没有使用它。此外,正如Robert在下面评论的那样,您应该使用此表单来明确区分您何时使用该属性:
@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
为什么你认为你有泄漏的物体?
答案 1 :(得分:0)
回顾一下我的观察结果:
我认为这不相关,但我认为你想要@synthesize photoList = _photoList
。现在,你有两个ivars,你明确声明的那个,_photoList
,以及一个从你的photoList属性photoList
隐式创建的。我也摆脱了_photoList ivar的现有显式声明,因为Apple当前建议你让合成声明为你做这件事。 (并且它会阻止这样的情况,在这种情况下你意外地结束了额外的ivars。)
此外,再次与泄漏无关(而是相反的问题),但jphoto正在过度发布,因为您同时发布了autorelease
和release
。后者就足够了。
此外,我没有看到您的sqlite3_finalize()
和sqlite3_close()
语句。他们在哪?根据{{3}},“必须使用对[sqlite3_finalize()
]的调用来销毁每个准备好的语句,以避免内存泄漏。”
最后,如果在为数据库添加finalize / close语句后仍然存在泄漏,我建议您通过分析器中的泄漏工具将代码运行到docs。这将帮助您准确识别泄漏的内容。