UIImageViews added to UIScollView never get deallocated, Memory Leak? - iphone

This is my first post and you are my last hope.
I have a list with images in my iPad app, if you select one, my class MyViewController which extends UIViewController is loaded and shown (by just setting window.rootViewController, by using UINavigationController, by presentModalViewController - tried everything, doesn't make any difference). It has a UIScrollView inside, which adds a big version of the image to the UIScrollView with this code:
UIImageView *tempImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[[delegate getImageNames] objectAtIndex:(gameNr*2)]]];
tempImageView.frame = CGRectMake(0,0,tempImageView.frame.size.width/2,tempImageView.frame.size.height/2);
[self setMyImage:tempImageView];
[tempImageView release];
myScrollView.contentSize = CGSizeMake(myImage.frame.size.width, myImage.frame.size.height);
myScrollView.maximumZoomScale = 2.0;
myScrollView.minimumZoomScale = 1.0;
myScrollView.clipsToBounds = YES;
myScrollView.bounces = NO;
myScrollView.delegate = self;
[myScrollView addSubview: myImage];
myImage is a (nonatomic, retain) property of the type UIImageView inside my MyViewController.
After pressing a button and go back to the list of images, I release the reference to MyViewController and it's dealloc method is called like this:
- (void)dealloc
{
CFShow(#"dealloc!");
for(UIImageView *subview in [myScrollView subviews]) {
if(subview.frame.size.width > 20){
[subview removeFromSuperview];
[subview setImage:nil];
}
}
[myImage setImage:nil];
self.myScrollView = nil;
self.myImage = nil;
[myScrollView release];
[myImage release];
[super dealloc];
}
It works so far, but the problem is, that the memory of the images is not really deallocated this way. After opening and closing about 12 images, the app crashes with memory warning.
This is also confirmed by the report_memory method I got from here:
iOS Low Memory Crash, but very low memory usage
The dealloc method IS called, that's triple checked.
I checked the variables myImage and myScrollViewvia breakpoint in the debugger, they are set to 0x0 by those commands in the dealloc method. Why is the memory not freed????????
Thanks for any suggestion, I am working on this since three whole days, it's driving me crazy.

Replace this:
self.myScrollView = nil;
self.myImage = nil;
[myScrollView release];
[myImage release];
with this:
[myScrollView release];
self.myScrollView = nil;
self.myImage = nil;
And post code where you define property myScrollView and init it.
And you don't need loop for(UIImageView *subview in [myScrollView subviews]). When you will release myScrollView object it will automatically release all its subviews.

Related

While releasing the UIView Object my app is crashing

I developed an iPhone project without XIB files. In that I manually created the UIView object and a few more components on that view also. I release these objects in my dealloc and viewDidUnload methods. My problem is that if I release the UIView object the app crashes and I do not know why. Can anyone help me in this issue?
Here is my code:
allmessengersview = [[UIView alloc] initWithFrame: [[UIScreen mainScreen] bounds]];
allmessengersview.backgroundColor = [UIColor whiteColor];
self.view = allmessengersview;
-(void) dealloc {
[allmessengersview release];
[super dealloc];
}
- (void) viewDidUnload {
[super viewDidUnload];
[allmessengersview release];
allmessengersview = nil;
}
Please Write Down this Code in Initialization in View
allmessengersview=[[UIView alloc]init];
allmessengersview.frame = CGRect (0,0,320,480);
allmessengersview.backgroundColor = [UIColor whiteColor];
[self.view addsubView: allmessengersview];
Please Write Down this Code in Dealloc Methods
-(void)dealloc
{
[allmessengersview release];
[super dealloc];
}
Don't release in viewDidUnload.
This way the app will release allmessengersview in the viewDidUnload and later on it tries to release it a second time in dealloc, but since it's already released, the app crashes.

Custom UIView Leaking Memory - iPhone/iPad/iOS App Development

I have a viewcontroller that repeatedly repositions 6 items within a uiscrollview. However, even though I've limited the number of items within the uiscrollview to 6, I'm still leaking memory when i update their position and their image. Can someone let me know if the following code which represents a unit within the uiscrollview is properly coded? startLoad is the method that I call after to reload the image.
#import "ScrollUnit.h"
#implementation ScrollUnit
#synthesize index;
#synthesize ProductToDisplay;
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code.
}
return self;
}
-(void)startLoad
{
[imageview removeFromSuperview];
[imageview release];
NSOperationQueue *queue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc]
initWithTarget:self
selector:#selector(loadImage)
object:nil];
[queue addOperation:operation];
[operation release];
}
-(void)loadImage
{
NSString *myimageName = [self.ProductToDisplay valueForKey:IMAGEKEY];
NSString *myimageUrl = [NSString stringWithFormat:#"%#/%#",IMAGE_SERVICE,myimageName];
NSData* imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:myimageUrl]];
UIImage* image = [[[UIImage alloc] initWithData:imageData] autorelease];
[imageData release];
[self performSelectorOnMainThread:#selector(displayImage:) withObject:image waitUntilDone:NO];
}
- (void)displayImage:(UIImage *)image
{
imageview = [[[UIImageView alloc] initWithFrame:CGRectMake(9, 0, 320, 320)]retain];
imageview.contentMode = UIViewContentModeScaleAspectFit;
imageview.backgroundColor = [UIColor whiteColor];
imageview.image = image;
[self addSubview:imageview];
[imageview release];
//[image release];
}
- (void)dealloc {
[super dealloc];
}
#end
Get rid of the retain message on this line and you should be all set:
imageview = [[[UIImageView alloc] initWithFrame:CGRectMake(9, 0, 320, 320)]retain];
The reason why you need to do this is because you already own the object by calling alloc, so at that point, the relative reference count is 2.
Once you invoke addSubview:, passing in the imageview, the reference count gets bumped to 3, then right back down to 2 once you release it on the next line.
So once that object gets sent release in -dealloc, you're still stuck because the reference count is now 1, not 0 as you expected.
There is also another little thing that might bug (or not). You don't release imageview before you assign it in displayImage method. As long as only startLoad is called you are fine but if displayImage is called from the outside then you still leak.
You might want to use a property with retain and then synthesis the getter and setter methods. This way the iOS will release your previous assignment before it retains your new assigned object. That said then you need to release created image view right were you create it and you have to use "self.imageview" in order to make sure that you use the setter (setImageview).

Why is my addSubview: method causing a leak?

Okay, so I have done a ton of research on this and have been pulling my hair out for days trying to figure out why the following code leaks:
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
UIImage *comicImage = [self getCachedImage:[NSString stringWithFormat:#"%#%#%#",#"http://url/",comicNumber,#".png"]];
self.imageView = [[[UIImageView alloc] initWithImage:comicImage] autorelease];
[self.scrollView addSubview:self.imageView];
self.scrollView.contentSize = self.imageView.frame.size;
self.imageWidth = [NSString stringWithFormat:#"%f",imageView.frame.size.width];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
Both self.imageView and self.scrollView are #propety (nonatomic, retain) and released in my dealloc.. imageView isn't used anywhere else in the code. This code is also run in a thread off of the main thread. If I run this code on my device, it will quickly run out of memory if I continually load this view. However, I've found if I comment out the following line:
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
UIImage *comicImage = [self getCachedImage:[NSString stringWithFormat:#"%#%#%#",#"http://url/",comicNumber,#".png"]];
self.imageView = [[[UIImageView alloc] initWithImage:comicImage] autorelease];
//[self.scrollView addSubview:self.imageView];
self.scrollView.contentSize = self.imageView.frame.size;
self.imageWidth = [NSString stringWithFormat:#"%f",imageView.frame.size.width];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
Memory usage becomes stable, no matter how many times I load the view. I have gone over everything I can think to see why this is leaking, but as far as I can tell I have all my releases straight. Can anyone see what I am missing?
You autorelease your imageview upon init, and then retain it by assigning it to self.imageView, then adding it as a subview retains it again. So, when the pool is drained, it gets a release message. When it is removed as a subview it gets a release message. Then if you dealloc, it gets a third release message. One of those three is not occurring. You say it's released in dealloc, so that's not it. The autorelease pool can be trusted to drain at some point, so that's not it. I would either make sure to remove it as a subview at some point, or get rid of one of your retain calls.
And.. shouldn't this:
self.imageView = [[[UIImageView initWithImage:comicImage] autorelease];
be this?:
self.imageView = [[UIImageView alloc] initWithImage:comicImage];
When calling this line:
[self.scrollView addSubview:self.imageView];
self.imageView is retained by its super view and when you don't need imageView anymore you should call:
[self.imageView removeFromSuperview];
This will call release on self.imageView.
p.s. You can track your ref counts by calling
NSLog(#"RefCount: %d", [self.imageView retainCount]);
add this line above
self.imageView = [[[UIImageView initWithImage:comicImage] autorelease];
to track the refCount. (Better option is to use Instruments but you already know that :))
Edit: It is a good practice to [[alloc] init] objects when you have retain properties like this:
UIView *myView = [[UIView alloc] init];
self.myCustomView = myView;
[myView release];
Otherwise you'll get self.myCustomView retained twice.
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
Fine.
UIImage *comicImage = [self getCachedImage:[NSString stringWithFormat:#"%#%#%#",#"http://url/",comicNumber,#".png"]];
Don't call methods get* anything unless you are following standard Cocoa patterns (which this method is not). Just call it cachedImage:.
self.imageView = [[[UIImageView initWithImage:comicImage] autorelease];
You are missing an alloc call; that should be:
self.imageView = [[[UIImageView alloc] initWithImage:comicImage] autorelease];
Or (if you want to avoid the autorelease pool; probably not an issue here):
UIImageView *iV = [[UIImageView alloc] initWithImage:comicImage];
self.imageView = iV;
[iV release];
[self.scrollView addSubview:self.imageView];
self.scrollView.contentSize = self.imageView.frame.size;
self.imageWidth = [NSString stringWithFormat:#"%f",imageView.frame.size.width];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
All fine. If there is a leak, it is either because imageView isn't released in dealloc or something else is hanging on to it (scrollView not being released, perchance?). Instruments can do a wonderful job of tracking down leaks, etc....
What the "leaks" tool looks for is objects that no longer have any references to them. In this case, it is quite likely that you have references remaining.
Frankly, given that you are easily able to reproduce the growth through repetition, Heapshot analysis will quite likely be highly applicable.
I wrote up a guide on Heapshot analysis a bit ago.

Memory management, release needed?

I wonder if someone could quickly help me with the following, do I need to add a [myTableView release]; after I call [view addSubview:[self myTableView]]; ? Initially I was thinking no, and running it through CLANG produced to memory warnings
Here is my thinking:
[self setMyTableView:tempTableView]; retainCount = (+1)
[view addSubview:[self myTableView]]; retainCount = (+2)
//[myTableView release]; << HERE retainCount = (+1)
-dealloc [myTableView release]; retainCount = ( 0)
.
#property (nonatomic, retain) UITableView *myTableView;
.
- (void)loadView {
NSLog(#"%s", __PRETTY_FUNCTION__);
[self setTitle:#"Location Data"];
CGRect viewFrame = CGRectMake(0, 20, 320, 460);
UIView *view = [[UIView alloc] initWithFrame:viewFrame];
CGRect tableFrame = CGRectMake(0, 0, 320, 416);
UITableView *tempTableView = [[UITableView alloc] initWithFrame:tableFrame];
[self setMyTableView:tempTableView];
[tempTableView release];
[view addSubview:[self myTableView]];
//[myTableView release]; << HERE
[[self myTableView] setDelegate:self];
[[self myTableView] setDataSource:self];
[self setView:view];
[view release];
}
.
- (void)dealloc {
[myTableView release];
[dataModel release];
[super dealloc];
}
EDIT: hmm maybe I don't as [view addSubview:[self myTableView]]; retains it and will release when done. Your right Carl, my bad. I was getting confused with: alloc, set, release, when this is simply the view taking ownership (and the responsibility to release that later)
You're right when you say that [view addSubview:[self myTableView]]; will retain your table view, but since it's the view who is retaining it, it's the view that is supposed to release it. And it will, when the view gets dealloc'ed. You only have to release what you retained yourself, i.e., you only have to release the table view once in your dealloc method.
Your method doesn't retain myTableView, so it shouldn't release it.

Problem releasing UIImageView after adding to UIScrollView

I'm having a memory problem related to UIImageView. After adding this view to my UIScrollView, if I try to release the UIImageView the application crashes. According to the stack trace, something is calling [UIImageView stopAnimating] after [UIImageView dealloc] is called. However, if I don't release the view the memory is never freed up, and I've confirmed that there remains an extra retain call on the view after deallocating...which causes my total allocations to climb quickly and eventually crash the app after loading the view multiple times. I'm not sure what I'm doing wrong here though...I don't know what is trying to access the UIImageView after it has been released. I've included the relevant header and implementation code below (I'm using the Three20 framework, if that has anything to do with it...also, AppScrollView is just a UIScrollView that forwards the touchesEnded event to the next responder):
Header:
#interface PhotoHiResPreviewController : TTViewController <UIScrollViewDelegate> {
NSString* imageURL;
UIImage* hiResImage;
UIImageView* imageView;
UIView* mainView;
AppScrollView* mainScrollView;
}
#property (nonatomic, retain) NSString* imageURL;
#property (nonatomic, retain) NSString* imageShortURL;
#property (nonatomic, retain) UIImage* hiResImage;
#property (nonatomic, retain) UIImageView* imageView;
- (id)initWithImageURL:(NSString*)imageTTURL;
Implementation:
#implementation PhotoHiResPreviewController
#synthesize imageURL, hiResImage, imageView;
- (id)initWithImageURL:(NSString*)imageTTURL {
if (self = [super init]) {
hiResImage = nil;
NSString *documentsDirectory = [NSString stringWithString:[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]];
[self setImageURL:[NSString stringWithFormat:#"%#/%#.jpg", documentsDirectory, imageTTURL]];
}
return self;
}
- (void)loadView {
[super loadView];
// Initialize the scroll view
hiResImage = [UIImage imageWithContentsOfFile:self.imageURL];
CGSize photoSize = [hiResImage size];
mainScrollView = [[AppScrollView alloc] initWithFrame:[UIScreen mainScreen].bounds];
mainScrollView.autoresizingMask = ( UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
mainScrollView.backgroundColor = [UIColor blackColor];
mainScrollView.contentSize = photoSize;
mainScrollView.contentMode = UIViewContentModeScaleAspectFit;
mainScrollView.delegate = self;
// Create the image view and add it to the scrollview.
UIImageView *tempImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, photoSize.width, photoSize.height)];
tempImageView.contentMode = UIViewContentModeCenter;
[tempImageView setImage:hiResImage];
self.imageView = tempImageView;
[tempImageView release];
[mainScrollView addSubview:imageView];
// Configure zooming.
CGSize screenSize = [[UIScreen mainScreen] bounds].size;
CGFloat widthRatio = screenSize.width / photoSize.width;
CGFloat heightRatio = screenSize.height / photoSize.height;
CGFloat initialZoom = (widthRatio > heightRatio) ? heightRatio : widthRatio;
mainScrollView.maximumZoomScale = 3.0;
mainScrollView.minimumZoomScale = initialZoom;
mainScrollView.zoomScale = initialZoom;
mainScrollView.bouncesZoom = YES;
mainView = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
mainView.autoresizingMask = ( UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
mainView.backgroundColor = [UIColor blackColor];
mainView.contentMode = UIViewContentModeScaleAspectFit;
[mainView addSubview:mainScrollView];
// Add to view
self.view = mainView;
[imageView release];
[mainScrollView release];
[mainView release];
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return imageView;
}
- (void)dealloc {
mainScrollView.delegate = nil;
TT_RELEASE_SAFELY(imageURL);
TT_RELEASE_SAFELY(hiResImage);
[super dealloc];
}
I'm not sure how to get around this. If I remove the call to [imageView release] at the end of the loadView method everything works fine...but I have massive allocations that quickly climb to a breaking point. If I DO release it, however, there's that [UIImageView stopAnimating] call that crashes the application after the view is deallocated.
Thanks for any help! I've been banging my head against this one for days. :-P
Cheers,
Josiah
Move the release of imageView before the point where you set it. This way you release the object before changing where the imageView pointer points to:
// Create the image view and add it to the scrollview.
UIImageView *tempImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, photoSize.width, photoSize.height)];
tempImageView.contentMode = UIViewContentModeCenter;
[tempImageView setImage:hiResImage];
// release old object
[imageView release];
// set the pointer to point at a new object
self.imageView = tempImageView;
[tempImageView release];
[mainScrollView addSubview:imageView];
Then at the bottom of your method where you release the other objects, remove the line:
[imageView release];
Ideally, class level variables should be released in the class' dealloc() method
Looks like imgView is a class level variable and you are releasing that at the end of the method call.
If you have a variable that you would like to release at the end of the method -- it should be a method-level variable. So, in your case, try making the imgView a method level variable. Then you should be able to release it at the end of the method without any problem
Why do you create a temporary view? Do you call loadView more than once?
self.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, photoSize.width, photoSize.height)];
[self.imageView setContentMode:UIViewContentModeCenter];
[self.imageView setImage:hiResImage];
[mainScrollView addSubview:self.imageView];
[self.imageView release]
Or release it in dealloc since it's an instance variable? addSubview retains your UIImageView, so it would be weird if it crashed because the object isn't present. Have you tried setting out breakpoints?