I am using Three20/TTThumbsviewcontroller to load photos. I am struggling since quite a some time now to fix memory leak in setting photosource. I am beginner in Object C & iOS memory management. Please have a look at following code and suggest any obvious mistakes or any errors in declaring and releasing variables.
-- 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];
}
Memory leaks happen when calling above LoadPhotosource method again with different query... I feel its something wrong in declaring NSMutableArray (photoList), but can't figure out how to fix memory leak. Any suggestion is really appreciated.
I havent seen any leaked objects in your code, but actually a double released object that will eventually crash your app:
In your last lines, you have this:
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];
If you take a closer look, you have a double release (autorelease in alloc and release after the addObject). You should remove one of them.
Besides that, I would also recommend you a few things:
Remove NSMutableArray *_photoList;
, you are not using it since you declare your property syntetizer as @synthesize photoList;
. In addition, as Robert commented below, you should use this form to clearly differentiate when you use the property:
@synthesize photoList = photoList_;
The underscore is better at the end because Apple uses it at the beginning and you dont want to accidentally use the same variable name.
Use property accessors to manage ivars. Instead of doing this:
if (photoList == nil)
photoList = [[NSMutableArray alloc] init ];
try this:
if (photoList == nil)
self.photoList = [[[NSMutableArray alloc] init] autorelease];
In your example both lines have the same behaviour, but the second one is preferable because it will rely on your variable memory policy. If you change your retain by a copy or an assign one day, your code will still work without any problem. In the same way, release your memory by assigning your ivar a nil like this self.photoList = nil
Why do you think you have a leaked object?