iphone image is leaking, but where? - iphone

the image that is being displayed in this code is leaking but I cant figure out how. What I have a tableview that displays images to be displayed. Each time a user selects an image, it should remove the old image, download a new one, then add it to the scroll view. But the old image is not being released and I cant figure out why...
-(void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[imageView removeFromSuperview];
self.imageView = nil;
NSUInteger row = [indexPath row];
NSString *tempC = [[NSString alloc]initWithFormat:#"http://www.website.com/%#_0001.jpg",[pdfNamesFinalArray objectAtIndex:row] ];
chartFileName = tempC;
pdfName = [pdfNamesFinalArray objectAtIndex:row];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *docsPath = [paths objectAtIndex:0];
NSString *tempString = [[[NSString alloc]initWithFormat:#"%#/%#.jpg",docsPath,pdfName]autorelease];
NSData *data = [NSData dataWithContentsOfFile:tempString];
if (data != NULL){
self.imageView = nil;
[imageView removeFromSuperview];
self.imageView = nil;
UIImageView *tempImage = [[[UIImageView alloc]initWithImage:[UIImage imageWithData:data]]autorelease];
self.imageView = tempImage;
[data release];
scrollView.contentSize = CGSizeMake(imageView.frame.size.width , imageView.frame.size.height);
scrollView.maximumZoomScale = 1;
scrollView.minimumZoomScale = .6;
scrollView.clipsToBounds = YES;
scrollView.delegate = self;
[scrollView addSubview:imageView];
scrollView.zoomScale = .37;
}
else {
[data release];
self.imageView = nil;
[imageView removeFromSuperview];
self.imageView = nil;
activityIndicator.hidden = NO;
getChartsButton.enabled = NO;
chartListButton.enabled = NO;
saveChartButton.enabled = NO;
[NSThread detachNewThreadSelector:#selector(downloadImages) toTarget:self withObject:nil];
}
chartPanel.hidden = YES;
}
-(void) downloadImages {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
self.imageView = nil;
[imageView removeFromSuperview];
NSURL *url = [[[NSURL alloc]initWithString:chartFileName]autorelease];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImageView *tempImage = [[[UIImageView alloc]initWithImage:[UIImage imageWithData:data]]autorelease];
self.imageView = tempImage;
tempImage = nil;
scrollView.contentSize = CGSizeMake(imageView.frame.size.width , imageView.frame.size.height);
scrollView.maximumZoomScale = 1;
scrollView.minimumZoomScale = .37;
scrollView.clipsToBounds = YES;
scrollView.delegate = self;
[scrollView addSubview:imageView];
scrollView.zoomScale = .6;
activityIndicator.hidden = YES;
getChartsButton.enabled = YES;
chartListButton.enabled = YES;
saveChartButton.enabled = YES;
[pool drain];
[pool release];
}

This looks wrong:
self.imageView = nil;
[imageView removeFromSuperview];
You are setting imageView to nil before removing it from the superview, so the 2nd statement is really just [nil removeFromSuperview];, which isn't going to do anything.

I think the leak is what David Gelhar said, but I just wanted to add that you shouldn't access UI stuff from threads other than the main thread (so for instance, don't do [imageView removeFromSuperview] under a separate thread). This can cause very odd problems, including mysterious leaks. Try putting all that stuff in a separate method on the main thread that you call with [self performSelectorOnMainThread:] and see if it still leaks.
Also (although this wouldn't cause the leak), [pool drain] releases the autorelease pool, so you shouldn't invoke [pool release] after it--it might release the pool on the main thread, possibly causing a crash somewhere down the line (since you could over-release the pool).

Related

ImageGallery image in black for ios 7

In my efforts to upgrade my application to support IOS7 I found out that ImageGallery don't load the images. in others iOS is ok.
In imageGalleryView:
- (void)initWithPhotos:(NSMutableArray *)photoURLStrings andCaptions:(NSArray *)myCaptions moveToPage:(int)page {
captions = [myCaptions copy];
photoUrls = [photoURLStrings copy];
NSLog(#"array---> %#", photoUrls);
photoLoaded = [[NSMutableArray alloc] init];
for (int i=0; i<[photoURLStrings count]; i++) {
[photoLoaded addObject:[NSNumber numberWithBool:FALSE]];
}
[pageControl setNumberOfPages:[photoUrls count]];
//scrollView= [[UIScrollView alloc] init];
scrollView.backgroundColor = [UIColor blackColor];
scrollView.pagingEnabled = TRUE;
scrollView.autoresizesSubviews = TRUE;
scrollView.contentSize = CGSizeMake(320 * [photoUrls count], scrollView.frame.size.height);
scrollView.contentOffset = CGPointMake(self.scrollView.frame.size.width*page, 0);
if (([captions objectAtIndex:page] == nil) || ([[captions objectAtIndex:page] isEqualToString:#""])) {
[textView setHidden:TRUE];
} else {
[textView setHidden:FALSE];
[textView setText:[captions objectAtIndex:page]];
}
[self showImages:page];}
- (void)showImages:(int)page {
AsyncImageViewController *asyncImageView;
if ((page>=0)&&(page<[photoUrls count])) {
if (![[photoLoaded objectAtIndex:page] boolValue]) {
[photoLoaded replaceObjectAtIndex:page withObject:[NSNumber numberWithBool:TRUE]];
asyncImageView = [[AsyncImageViewController alloc] init];
//NSLog(#"%#",[NSString stringWithFormat:#"%#",[photoUrls objectAtIndex:page]]);
[asyncImageView loadImageFromURL:[NSURL URLWithString:[NSString stringWithFormat:#"%#",[photoUrls objectAtIndex:page]]] pos:1];
//NSLog(#"page:%i",page);
asyncImageView.frame = CGRectMake(320*page,0,320,scrollView.frame.size.height);
//[scrollView setBackgroundColor:[UIColor colorWithPatternImage:asyncImageView.image]];
[scrollView addSubview:asyncImageView];
[asyncImageView release];
}
}
page = page - 1;
if ((page>=0)&&(page<[photoUrls count])) {
if (![[photoLoaded objectAtIndex:page] boolValue]) {
[photoLoaded replaceObjectAtIndex:page withObject:[NSNumber numberWithBool:TRUE]];
asyncImageView = [[AsyncImageViewController alloc] init];
[asyncImageView loadImageFromURL:[NSURL URLWithString:[NSString stringWithFormat:#"%#",[photoUrls objectAtIndex:page]]] pos:1];
asyncImageView.frame = CGRectMake(320*page,0,320,scrollView.frame.size.height);
[scrollView addSubview:asyncImageView];
[asyncImageView release];
}
}
page = page + 2;
if ((page>=0)&&(page<[photoUrls count])) {
if (![[photoLoaded objectAtIndex:page] boolValue]) {
[photoLoaded replaceObjectAtIndex:page withObject:[NSNumber numberWithBool:TRUE]];
asyncImageView = [[AsyncImageViewController alloc] init];
[asyncImageView loadImageFromURL:[NSURL URLWithString:[NSString stringWithFormat:#"%#",[photoUrls objectAtIndex:page]]] pos:1];
asyncImageView.frame = CGRectMake(320*page,0,320,scrollView.frame.size.height);
[scrollView addSubview:asyncImageView];
//[scrollView setBackgroundColor:[UIColor colorWithPatternImage:asyncImageView.image]];
[asyncImageView release];}}}
In the asyncimageview:
- (void)loadImageFromURL:(NSURL*)url pos:(int) posicio {
if (connection!=nil) { [connection release]; }
if (data!=nil) { [data release]; }
NSURLRequest* request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
loading = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
if (posicio == 0) {
loading.center = CGPointMake(75/2,75/2);
}else {
loading.center = CGPointMake(160,210);
}
[loading startAnimating];
[self addSubview:loading];
[loading release];}//the URL connection calls this repeatedly as data arrives- (void)connection:(NSURLConnection *)theConnection didReceiveData:(NSData *)incrementalData {
if (data==nil) { data = [[NSMutableData alloc] initWithCapacity:2048]; }
[data appendData:incrementalData];}//the URL connection calls this once all the data has downloaded- (void)connectionDidFinishLoading:(NSURLConnection*)theConnection {
//so self data now has the complete image
[connection release];
connection=nil;
if ([[self subviews] count]>0) {
//then this must be another image, the old one is still in subviews
[[[self subviews] objectAtIndex:0] removeFromSuperview]; //so remove it (releases it also)
}//make an image view for the image
imageView = [[[UIImageView alloc] initWithImage:[UIImage imageWithData:data]] autorelease];
//make sizing choices based on your needs, experiment with these. maybe not all the calls below are needed.
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.autoresizingMask = ( UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight );
//[self setBackgroundColor:[UIColor colorWithPatternImage:imageView]];
[self addSubview:imageView];
imageView.frame = self.bounds;
[imageView setNeedsLayout];
[self setNeedsLayout];
[loading stopAnimating];
[data release]; //don't need this any more, its in the UIImageView now
data=nil;}
//just in case you want to get the image directly, here it is in subviews- (UIImage*) image {
UIImageView* iv = [[self subviews] objectAtIndex:0];
return [iv image];}
I checked all and saw that it is UIView instead of UIImageView. probably Apple changed something. But xCode don't throw any errors.
Any idea how to fix it?

How do I keep the keyboard hidden when it's not wanted?

The app I'm working on allows the user both to read text and to compose text, using different buttons. My problem now is that on the screen for reading text, if the user taps inside the UITextView box used on the screen for writing text, the keyboard appears. The UITextView in this case is self.textView; I've put the keyboard notifications and the keyboardWillShow method inside if(self.textView) statements and then made sure to call [self.textView removeFromSuperView] and set self.textView = nil; at the beginning of the reading text methods, but the keyboard still appears when you tap the space where self.textView is set (programmatically, by the way, not using the IB).
What am I doing wrong?
Edit: Thanks for the answers, guys and gals, but still that darn keyboard keeps coming back, just like the cat in the song.... Here's my code. Forgive its length, please, if you can; if I've done something wonky I don't know where it is, and so I don't know what to leave out.
Here's viewDidLoad.
-(void)viewDidLoad {
[super viewDidLoad];
self.textView.editable=NO;
self.textView.userInteractionEnabled = NO;
UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(previousHaiku)];
swipeRight.numberOfTouchesRequired = 1;
swipeRight.direction = UISwipeGestureRecognizerDirectionRight;
[self.view addGestureRecognizer:swipeRight];
UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(nextHaiku)];
swipeLeft.numberOfTouchesRequired = 1;
swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
[self.view addGestureRecognizer:swipeLeft];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"gayHaiku.plist"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath: path])
{
NSString *bundle = [[NSBundle mainBundle] pathForResource:#"gayHaiku" ofType:#"plist"];
[fileManager copyItemAtPath:bundle toPath: path error:&error];
}
self.gayHaiku = [[NSMutableArray alloc] initWithContentsOfFile: path];
[self nextHaiku];
}
Here's nextHaiku, the last method called in viewDidLoad -- this is the reading method.
-(void)nextHaiku
{
[self.view.layer removeAllAnimations];
[self.bar removeFromSuperview];
self.textToSave=#"";
self.haiku_text.text=#"";
[self.view viewWithTag:1].hidden = NO;
[self.view viewWithTag:3].hidden = NO;
int indexOfHaiku;
NSMutableArray *arrayOfHaikuSeen;
NSString *cat;
if (!self.selectedCategory) cat = #"Derfner";
else cat = self.selectedCategory;
NSArray *filteredArray;
if (cat==#"all")
{
filteredArray = self.gayHaiku;
indexOfHaiku = self.indxAll;
arrayOfHaikuSeen = self.theseAreDoneAll;
}
else
{
indexOfHaiku = (cat==#"user")?self.indxU:self.indxD;
arrayOfHaikuSeen = (cat==#"user")?self.theseAreDoneU:self.theseAreDoneD;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"category == %#", cat];
filteredArray = [self.gayHaiku filteredArrayUsingPredicate:predicate];
}
int array_tot = [filteredArray count];
int sortingHat;
NSString *txt;
if (array_tot > 0)
{
if (indexOfHaiku == arrayOfHaikuSeen.count)
{
while (true)
{
sortingHat = (arc4random() % array_tot);
if (![arrayOfHaikuSeen containsObject:[filteredArray objectAtIndex:sortingHat]]) break;
}
txt = [[filteredArray objectAtIndex:sortingHat] valueForKey:#"quote"];
if (!arrayOfHaikuSeen || arrayOfHaikuSeen.count==array_tot)
{
arrayOfHaikuSeen = [[NSMutableArray alloc] init];
}
[arrayOfHaikuSeen addObject:[filteredArray objectAtIndex:sortingHat]];
indexOfHaiku = arrayOfHaikuSeen.count;
if (arrayOfHaikuSeen.count==filteredArray.count)
{
[arrayOfHaikuSeen removeAllObjects];
indexOfHaiku=0;
}
}
else
{
txt = [[arrayOfHaikuSeen objectAtIndex:indexOfHaiku] valueForKey:#"quote"];
indexOfHaiku += 1;
}
}
//Need to test to make sure it starts over once all 110 haiku have been seen.
CGSize dimensions = CGSizeMake(320, 400);
CGSize xySize = [txt sizeWithFont:[UIFont fontWithName:#"Helvetica" size:14.0] constrainedToSize:dimensions lineBreakMode:0];
self.haiku_text = [[UITextView alloc] initWithFrame:CGRectMake((320/2)-(xySize.width/2),200,320,200)];
self.haiku_text.font = [UIFont fontWithName:#"Helvetica Neue" size:14];
self.haiku_text.backgroundColor = [UIColor clearColor];
self.haiku_text.text=txt;
CATransition *transition = [CATransition animation];
transition.duration = 0.25;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush;
transition.subtype =kCATransitionFromRight;
transition.delegate = self;
[self.view.layer addAnimation:transition forKey:nil];
[self.view addSubview:self.haiku_text];
if (cat==#"user")
{
self.theseAreDoneU = arrayOfHaikuSeen;
self.indxU = indexOfHaiku;
}
else if (cat==#"all")
{
self.theseAreDoneAll = arrayOfHaikuSeen;
self.indxAll = indexOfHaiku;
}
else
{
self.theseAreDoneD = arrayOfHaikuSeen;
self.indxD = indexOfHaiku;
}
}
And here's the writing method.
-(void)userWritesHaiku
//Set up the screen.
[self clearScreen];
self.textView = [[UITextView alloc] initWithFrame:CGRectMake(20, 60, 280, 150)];
self.textView.delegate = self;
self.textView.returnKeyType = UIReturnKeyDefault;
self.textView.keyboardType = UIKeyboardTypeDefault;
self.textView.font = [UIFont fontWithName:#"Helvetica Neue" size:14];
self.textView.scrollEnabled = YES;
self.textView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
self.textView.userInteractionEnabled = YES;
self.textView.editable=YES;
self.textView.backgroundColor = [UIColor colorWithRed:217 green:147 blue:182 alpha:.5];
[self.view addSubview: self.textView];
[self loadNavBar:#"Compose"];
[self addLeftButton:#"Instructions" callingMethod:#"haikuInstructions"];
//If you've added text before calling haikuInstructions, when you return from haikuInstructions the textView window with the different background color AND the keyboard.
[self addRightButton:#"Done" callingMethod:#"userFinishedWritingHaiku"];
self.titulus.hidesBackButton=YES;
[self seeNavBar];
//Create and add the space for user to write.
[self createSpaceToWrite];
if (self.textToSave!=#"")
{
self.textView.text = self.textToSave;
}
[self.view addSubview:self.textView];
[self.textView becomeFirstResponder];
//Keyboard notifications.
if (self.textView)
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
}
try this
[self.textView setEditable:NO];
When the user taps on the reading button, I would just call
self.textView.userInteractionEnabled = NO;
and when you want them to edit it call
self.textView.userInteractionEnabled = YES;
Well, it turned out that the whole time there was a UITextView I'd created in the xib and forgotten about because it was hiding under the main view--when I decided to create the UITextView programmatically I didn't remember to delete the other one, because I couldn't see it, but it was there working its evil will the entire time. I finally figured this out after commenting out literally the entire code with the exception of [super viewDidLoad] and removing everything from the xib.

Application shows low memory warning and crashes while loading images?

I am using following code for loading images from server using following code.When i scroll UITableView application crashes.
AsynchrohousImageView class .m file
- (void)dealloc {
[connection cancel]; //in case the URL is still downloading
[connection release];
[data release];
[_imageView release];
[_activityIndicator release];
[super dealloc];
}
- (void)loadImageFromURL:(NSURL*)url
defaultImageName:(NSString *)defaultImageName
showDefaultImage:(BOOL)defaultImageIsShown
showActivityIndicator:(BOOL)activityIndicatorIsShown
activityIndicatorRect:(CGRect)activityIndicatorRect
activityIndicatorStyle:(UIActivityIndicatorViewStyle)activityIndicatorStyle {
if (connection!=nil) { [connection release]; } if (data!=nil) { [data release]; }
if ([[self subviews] count]>0) {
[[[self subviews] objectAtIndex:0] removeFromSuperview]; // }
if (defaultImageIsShown) {
self.imageView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:defaultImageName]] autorelease];
} else {
self.imageView = [[[UIImageView alloc] init] autorelease];
}
[self addSubview:_imageView];
_imageView.frame = self.bounds;
[_imageView setNeedsLayout];
[self setNeedsLayout];
if (activityIndicatorIsShown) {
self.activityIndicator = [[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:activityIndicatorStyle] autorelease];
[self addSubview:_activityIndicator];
_activityIndicator.frame = activityIndicatorRect;
_activityIndicator.center = CGPointMake(_imageView.frame.size.width/2, _imageView.frame.size.height/2);
[_activityIndicator setHidesWhenStopped:YES];
[_activityIndicator startAnimating];
}
NSURLRequest* request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
}
- (void)connection:(NSURLConnection *)theConnection didReceiveData:(NSData *)incrementalData {
if (data==nil) { data = [[NSMutableData alloc] initWithCapacity:2048]; }
[data appendData:incrementalData];
}
- (void)connectionDidFinishLoading:(NSURLConnection*)theConnection {
[connection release];
connection=nil;
_imageView.image = [UIImage imageWithData:data];
if (_activityIndicator) {
[_activityIndicator stopAnimating];
}
[data release]; data=nil;
}
- (UIImage*) image {
UIImageView* iv = [[self subviews] objectAtIndex:0];
return [iv image];
}
In ViewController Class Which loads image
- (UITableViewCell *)tableView:(UITableView *)tV cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *reuseIdentifier =#"CellIdentifier";
ListCell *cell = (ListCell *)[tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
if (cell==nil) {
cell = [[ListCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier];
NSMutableDictionary *dicResult = [arrResults objectAtIndex:indexPath.row];
NSURL *url=[NSURL URLWithString:[dicResult objectForKey:#"Image"]];
AsynchronousImageView *asyncImageView = [[AsynchronousImageView alloc] initWithFrame:CGRectMake(5, 10,80,80)];
[asyncImageView loadImageFromURL:url
defaultImageName:#"DefaultImage.png"
showDefaultImage:NO
showActivityIndicator:YES
activityIndicatorRect:CGRectMake(5, 10,30,30)
activityIndicatorStyle:UIActivityIndicatorViewStyleGray]; // load our image with URL asynchronously
[cell.contentView addSubview:asyncImageView];
// cell.imgLocationView.image = [UIImage imageNamed:[dicResult valueForKey:#"Image"]];
[asyncImageView release];
}
if([arrResults count]==1)
{
UITableViewCell *cell1=[tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
if(cell1==nil)
cell1=[[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier] autorelease];
NSMutableDictionary *dicResult = [arrResults objectAtIndex:0];
cell1.textLabel.text=[dicResult valueForKey:#"NoResults"];
return cell1;
}
else
{
NSMutableDictionary *dicResult = [arrResults objectAtIndex:indexPath.row];
NSString *title = [NSString stringWithFormat:#"%# Bedrooms-%#", [dicResult valueForKey:KEY_NUMBER_OF_BEDROOMS],[dicResult valueForKey:KEY_PROPERTY_TYPE]];
NSString *strAddress = [dicResult valueForKey:KEY_DISPLAY_NAME];
NSString *address = [strAddress stringByReplacingOccurrencesOfString:#", " withString:#"\n"];
NSString *price = [dicResult valueForKey:KEY_PRICE];
NSString *distance = [dicResult valueForKey:KEY_DISTANCE];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.lblTitle.text = title;
cell.lblAddress.text = address;
if ([price length]>0) {
cell.lblPrice.text = [NSString stringWithFormat:#"£%#",price];
}else{
cell.lblPrice.text = #"";
}
if ([distance length]>0) {
cell.lblmiles.text = [NSString stringWithFormat:#"%.2f miles",[distance floatValue]];
}else{
cell.lblmiles.text = #"";
}
}
return cell;
}
How can i resolve this?
I have attached heapshot analysis screen shot of it.Here non Object consumes so much of memory what is that?
this is the error:
NSString *reuseIdentifier = [NSString stringWithFormat:#"%d",indexPath.row];
it seems you are NOT reusing cells, but creating a new cell for every row of your table!!!
this way if you need to see 100 or 1000 rows, you create/allocate 100 or 1000 object cells.
that's not the right use of a UITableView.
the "magic" of UITableView is that it reuse cells, and it just creates and allocates just the minor number of cells needed...
e.g. consider you have a vertical spaces of 480 pixels for your tables, and your cells are 100 pixel height, then you just need 5 cells for time, no need to create 1000 cells, you can see just 5 cells at time...
so the magic is to reuse an already allocated cell when you scroll it up and it goes out of screen, and to reset it's contents (images and text) and to use it for the new call that user is going to see down...
While cell reuse is not the problem, leaking cells is:
cell = [[ListCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier];
You forgot to autorelease this, so you're leaking cells very quickly. You did remember to autorelease cell1.

iPhone - Objective-C NSURL Memory Leaks galore

I am getting the following memory leak when using NSURL. I use this method in quite a few different places and receive memory leaks all the time using the Leaks instruments.
Object Management:
self.objManager = [[HJObjManager alloc] init];
NSString *cacheDirectory = [NSHomeDirectory() stringByAppendingPathComponent:#"/Library/Caches/App"];
HJMOFileCache *fileCache = [[[HJMOFileCache alloc] initWithRootPath:cacheDirectory] autorelease];
self.objManager.fileCache = fileCache;
fileCache.fileCountLimit = 100;
fileCache.fileAgeLimit = 60*60*24;
[fileCache trimCacheUsingBackgroundThread];
Where it's used:
HJManagedImageV *mi = [[[HJManagedImageV alloc] initWithFrame:CGRectMake(self.myHeaderView.profilePictureImageView.bounds.origin.x,
self.myHeaderView.profilePictureImageView.bounds.origin.y,
self.myHeaderView.profilePictureImageView.bounds.size.width,
self.myHeaderView.profilePictureImageView.bounds.size.height)] autorelease];
mi.layer.masksToBounds = YES;
mi.layer.cornerRadius = 8.0;
mi.layer.borderColor = [[UIColor blackColor] CGColor];
mi.layer.borderWidth = 1.0;
mi.url = [NSURL URLWithString:profilePictureUrl];
[mi showLoadingWheel];
[self.myHeaderView.profilePictureImageView addSubview:mi];
[self.objManager manage:mi];
Dealloc:
- (void)viewDidUnload {
self.tv = nil;
self.friends = nil;
self.sortedFriends = nil;
self.detailView = nil;
self.profileView = nil;
self.objManager = nil;
[super viewDidUnload];
}
- (void)dealloc {
[tv release];
[friends release];
[sortedFriends release];
[detailView release];
[profileView release];
[objManager release];
[super dealloc];
}
You can use stack trace to determine the place of leak.
Edit:
make sure you are releasing the mi.url in dealloc method:
-(void) dealloc {
//some other releases
self.url = nil;
[super dealloc];
}

Memory allocation, release and NSURLConnection in iPhone app

I'm hoping someone can help me with this. I'm struggling to find an answer to what should be an easy question. By the way, this is my first major obj-c project after years of using c and c# so feel free to point out things I'm failing on.
I have an iPhone app designed to load an album of photos into a UIScrollView. It also has a random image function which uses the same process but only displays a single image. It works like so:
Read an external XML feed (stored on ruby-on-rails website) which contains a path to a random url of photo once parsed.
Content of URL is downloaded using NSURLConnection to NSData.
ScrollView is created and pushed
Subclassed UIView allocs an UIImageView, allocs a UIImage with the NSData which, inits the UIImageView with the UIimage and finally adds the imageview to the UIView.
The parent view then adds the UIView to the UIScrollView which is then pushed to the front.
This process occurs again when when the next random image is required. It also uses the same process when displaying an entire album of images except several UIViews are added to the UIScrollView.
The problem is, despite using release and delloc where appropriate, the activity tool indicates that the memory used by NSURLConnection and UIImage isn't being released from memory when the next image is requested. This is further proven by building the app to the iPhone. Requesting several images in a row causes the app the crash presumably from memory consumption.
Below is some of the code as I'm unable to post the entire project due to contractual agreements.
URLDownload class (uses DataDownload)
#implementation URLDownload
-(NSData *)GetURL:(NSURL *)strURL
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
DataDownload *request = [DataDownload alloc];
request.strURL = strURL;
[request init];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
while (request.isLoading && [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
[pool release];
return [request urlData];
[request release];
}
DataDownload class
#implementation DataDownload
#synthesize urlData, connection, strURL, isLoading;
- (id)init
{
if(self = [super init])
{
self.isLoading = YES;
NSURLRequest *dataRequest = [NSURLRequest requestWithURL:self.strURL
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:60];
/* establish the connection */
self.connection = [[NSURLConnection alloc]
initWithRequest:dataRequest
delegate:self
];
if (connection == nil) {
self.urlData = nil;
} else {
self.urlData = [NSMutableData data];
}
}
return self;
}
- (void)dealloc {
[connection cancel];
[connection release];
[urlData release];
[strURL release];
[super dealloc];
}
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse {
return nil;
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse*)response
{
[urlData setLength:0];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData*)incrementalData
{
[self.urlData appendData:incrementalData];
}
-(void)connectionDidFinishLoading:(NSURLConnection*)connection
{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
self.isLoading = NO;
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
self.isLoading = NO;
}
#end
ImageView subclass
#implementation ImageView
#synthesize strURL, imvImageView, image, currentlyRotated, imgOptionsView, startDate;
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.imvImageView = [UIImageView alloc];
if (self.strURL != nil){
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
NSData *datImageData = [NSData dataWithContentsOfURL: [NSURL URLWithString:self.strURL]];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
self.image = [UIImage imageWithData: datImageData];
CGSize imgSize = image.size;
CGFloat fltWidth = imgSize.width;
CGFloat fltHeight = imgSize.height;
[imvImageView initWithImage:image];
// If landscape rotate image
if (fltWidth > fltHeight){
imvImageView.frame = CGRectMake(-80.0, 80.0, 480.0, 320.0);
CGAffineTransform rotate = CGAffineTransformMakeRotation(-1.57079633);
[imvImageView setTransform:rotate];
self.currentlyRotated = YES;
}else{
imvImageView.frame = CGRectMake(0.0, 0.0, 320.0, 480.0);
self.currentlyRotated = NO;
}
[image release];
}else{
self.image = [UIImage alloc];
[imvImageView initWithImage:image];
[image release];
}
[self addSubview:imvImageView];
}
[imvImageView release];
return self;
}
- (void)drawRect:(CGRect)rect {
// Drawing code
}
- (void)dealloc {
[strURL release];
[imvImageView release];
[image release];
[imgOptionsView release];
[startDate release];
[super dealloc];
}
Sample code of how images are being displayed
- (void)displayImage:(NSString *)strURL {
NSString *strFullURL = #"http://www.marklatham.co.uk";
strFullURL = [strFullURL stringByAppendingString:strURL];
self.scroller = [[UIScrollView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 480.0)];
[scroller setContentSize:CGSizeMake(320.0, 540.0)];
[self.view addSubview:scroller];
CGRect rect = CGRectMake(0.0, 0.0, 320.0, 480.0);
self.imageView = [ImageView alloc];
imageView.strURL = strFullURL;
[imageView initWithFrame:rect];
[scroller addSubview:imageView];
[scroller release];
[imageView release];
}
The following images show all allocations to illustrate what's happening. 1 shows alloc on startup, 2 shows after first image is loaded and 3 after after second image.
http://www.gretanova.com/misc/iphone/1.png & 2.png & 3.png
Thanks everyone,
Lee
A couple of things stick out. For example within the URLDownload class the call to [request release] will never be reached as its placed after the return statement
return [request urlData];
[request release];