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

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).

Related

memory leak in singleton class

I am new to iPhone development.
I am developing an application in which I am using Single tone class.
When I am creating an object of single tone class it is giving me memory leak on analyzing my code. It is giving message as "Potential leak of an object" and "Allocated object is not referenced later". But I am using that object in my code.
following is my code where I have created single tone class object
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"Inside View");
self.navigationController.navigationBar.topItem.title = #"Menu List";
UIImage *image = [UIImage imageNamed:#"Navigation_bar.png"];
[_bgImage setFrame:CGRectMake(0,-45,320,510)];
[self.navigationController.navigationBar setBackgroundImage:image
forBarMetrics:UIBarMetricsDefault];
[self.tabBarController.tabBar setBackgroundImage:[UIImage imageNamed:#"Tab_bar.png"]];
[self.navigationItem setHidesBackButton:YES];
menuTableView.backgroundColor=[UIColor clearColor];
menuTableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
_hotelMenu=[SharedHotelMenu sharedInstanceMethod];
_queryFormatter=[[DatabaseQueryFormatter alloc]init];
_isSearchOn=NO;
_searchResult=[[NSMutableArray alloc]init];
_categorySearch.layer.cornerRadius = 19;
_categorySearch.clipsToBounds = YES;
_categorySearch.delegate=self;
UIView *_paddingView=[[UIView alloc] initWithFrame:CGRectMake(0,0,5,10)];
_categorySearch.leftView=_paddingView;
_categorySearch.leftViewMode=UITextFieldViewModeAlways;
[_paddingView release];
UIView *_paddingRightView=[[UIView alloc] initWithFrame:CGRectMake(0,0,30,10)];
_categorySearch.rightView=_paddingRightView;
_categorySearch.rightViewMode=UITextFieldViewModeAlways;
[_paddingRightView release];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(searchBar)
name:UITextFieldTextDidChangeNotification object:_categorySearch];
}
}
I have created single tone class object as _hotelMenu=[SharedHotelMenu sharedInstanceMethod];
As far as I can see in your code is that this line may cause memory leak
menuTableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
As this is #property(nonatomic, retain) UIView *tableFooterView and it will retain your object and thus retain count becomes 2.
Use this
UIView *footerView = [[UIView alloc] initWithFrame:CGRectZero];
menuTableView.tableFooterView = footerView;
[footerView release];

iOS memory leaks causing crash in changing view controllers

I have created two view controllers in which there is UIImage animations.Its crashing frequently and showing memory leaks in xcode instrument.
My Controllers Code-
- (void)viewDidLoad {
NSArray *firstArray;
firstArray = [NSArray arrayWithObjects:
[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"up0001" ofType:#"png"]],
[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"up0002" ofType:#"png"]],
::
::
[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"up0035" ofType:#"png"]], nil];
imgView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 1024, 768)];
imgView = UIViewContentModeScaleToFill;
[imgView setAnimationImages:firstArray];
imgView.animationDuration = 1.75;
imgView.animationRepeatCount = 0;
[imgView startAnimating];
[self.view addSubview: imgView];
}
- (void)dealloc {
[super dealloc];
[imgView release];
imgView = nil;
}
And I am changing viewcontrollers as my rootviewcontroller by getting appdelegate object and calling following appdelegate function in my Appdelegate.m(Please suggest any good approach)
- (void)changeRootViewController:(NSString *)controllerName
{
if(self.viewController){
[self.viewController.view removeFromSuperview];
self.viewController=nil;
}
if (controllerName == #"ViewController") {
ViewController *lviewController =[[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
self.viewController = (RootViewController *)lviewController;
[lviewController release];
lviewController.view = nil;
[self.window setRootViewController:self.viewController]; //LEAKS 100%
} else if (controllerName == #"MainViewController") {
// Use a different VC as roowViewController
MainViewController *lviewController =[[MainViewController alloc] initWithNibName:#"MainViewController" bundle:nil];
self.viewController = (RootViewController *)lviewController;
[lviewController release];
lviewController.view = nil;
[self.window setRootViewController:self.viewController]; //LEAKS 100%
} else if (controllerName == #"SecondViewController") {
// Use a different VC as roowViewController
SecondViewController *lviewController =[[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
self.viewController = (RootViewController *)lviewController;
[lviewController release];
lviewController.view = nil;
[self.window setRootViewController:self.viewController]; //LEAKS 100%
}
[self.window makeKeyAndVisible];
}
And calling this in my respective controlers button pressed as -
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
[appDelegate changeRootViewController:#"ViewController"];
OR
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
[appDelegate changeRootViewController:#"MainViewController"];
I want to manage view controllers from my main controller swapping controllers in and out without Navigationbar.Please help me figure out the best possible approach and avoid leaks as well.
Look at this piece of code:
if (controllerName == #"ViewController") {
ViewController *lviewController =[[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
self.viewController = (RootViewController *)lviewController;
[lviewController release];
lviewController.view = nil;
[self.window setRootViewController:self.viewController]; //LEAKS 100%
}
When you alloc "lviewController", its retain count is 1;
when you do "self.viewController" as I suppose viewController is a retained property, then lviewController retain count is increased to 2;
then you release it, balancing the previous alloc, and retain count returns to 1;
finally you assign it to rootViewController, which is a retained property, so again lviewController retain count is 2;
finally when you "swap" view controller in your window, the lviewController is released, so it retain count becomes 1. As you can see, it will never be dealloc'd. This means that each time you call this function, you make a leak.
You require to change some of your statement in your code ..
This line have no meaning ..
imgView = UIViewContentModeScaleToFill;
There is a function by using that you have to set the value of the UIImageView property to UIViewContentModeScaleToFill find that in a UIImageView documentation.
[imageView setContentMode:UIViewContentModeScaleToFill];
The same with this...
lviewController.view = nil; //Remove this from your code ..
And finally change the implmentation for the dealloc function and remember [super dealloc]; should be in the last in any implementation of dealloc .
- (void)dealloc {
[imgView release];
imgView = nil;
[super dealloc];
}
The probable leak is due to the code inside "SecondViewController", "MainViewController", "ViewController" classes. And the reason for crash is you are releasing image view after [super dealloc]
try this,
- (void)dealloc {
[imgView release];
imgView = nil;
[super dealloc];
}

UIImageViews added to UIScollView never get deallocated, Memory Leak?

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.

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.

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?