iphone memory management: alloc and retain properties - iphone

According to the docs, you do one release per alloc or retain (etc)
However what about when using retain propertys?
eg:
HEADER
#property(retain)UIView *someView;
IMPLEMENTATION
/*in some method*/
UIView *tempView = [[UIView alloc] init]; //<<<<<ALLOC - retain count = +1
[tempView setBackgroundColor:[UIColor redColor]];
self.someView = tempView; ///<<<<<RETAIN - retain count = +2
[tempView release]; ///should I do this?
or a different version of the IMPLEMENTATION
self.someView = [[UIView alloc] init]; //<<<<<ALLOC & RETAIN - retain count = +2
//now what??? [self.someView release]; ????
EDIT: I didn't make it clear, but I meant what to do in both circumstances, not just the first.

/*in some method*/
UIView *tempView = [[UIView alloc] init]; //<<<<<ALLOC - retain count = +1
[tempView setBackgroundColor:[UIColor redColor]];
self.someView = tempView; ///<<<<<RETAIN - retain count = +2
[tempView release]; ///should I do this? // YES!!!!
And you should also release all retain properties in your dealloc method, before [super dealloc].

Your first version is correct. There's only one ongoing reference to the view, so a retain count of 1 is appropriate.

For the second sample, you can use autorelease:
self.someView = [[[UIView alloc] init] autorelease];

Related

Is it ok to allocate a UIView twice and am I releasing it properly?

I wish to create multiple instances of UIView so i figured instead of creating new variables i'd allocate one UIView and then reallocate it again to create another UIView. Is this ok? Plus am I releasing the view properly or is the retain count of tempview is 2 after 2 allocations and a release just brings the retain count to 1?
NSMutableArray *array = [[NSMutableArray alloc] init];
UIView *tempview = [[UIView alloc] initWithFrame:CGRectMake(15, 30, 320, 460)];
[array addObject:tempView];
tempview = [[UIView alloc] initWithFrame:CGRectMake(15, 30, 320, 460)];
[array addObject:tempView];
[tempview release];
[array release];
You need to release tempView before you re-assign it or else it will leak.
NSMutableArray *array = [[NSMutableArray alloc] init];
UIView *tempview = [[UIView alloc] initWithFrame:CGRectMake(15, 30, 320, 460)];
[array addObject:tempView];
[tempView release]; //you need this to avoid leaking at the next line
tempview = [[UIView alloc] initWithFrame:CGRectMake(15, 30, 320, 460)];
[array addObject:tempView];
[tempview release];
[array release];
Alternately, you could autorelease tempView each time you alloc/init it, but it's better to release when you can and only use autorelease when you have to.
also, if all views you create have the same frame you might do the same in a loop:
const int kViewCount = 8;
NSMutableArray * array = [[NSMutableArray alloc] initWithCapacity:kViewCount];
for(int i = 0; i < kViewCount; ++i)
{
UIView *tempview = [[UIView alloc] initWithFrame:CGRectMake(15, 30, 320, 460)];
[array addObject:tempView];
[tempView release];
}
just set kViewCount to the number of views you need to create

Will releasing an NSMutableArray having allocated UIViews release the UIViews as well?

Ok so here's what I'm doing.
NSMutableArray *array = [[NSMutableArray alloc] init];
UIView *tempview = [[UIView alloc] initWithFrame:CGRectMake(15, 30, 320, 460)];
[array addObject:tempView];
UIView *tempview2 = [[UIView alloc] initWithFrame:CGRectMake(15, 30, 320, 460)];
[array addObject:tempView2];
[array release];
Will the releasing of the array, release the two allocated UIViews as well?
If you copy, alloc, retain, or new something, you are responsible for sending it either release or autorelease.
You said [[UIView alloc] init...] so you must release the resulting object.
You are responsible for releasing the views since you created them. Here is how it goes:
You create the views with a retain count of 1.
When they are added to the array, it will retain them (retain count = 2).
When you release the array, it will release the views (retain count = 1).
You still need to release them.
The correct code would be:
NSMutableArray *array = [[NSMutableArray alloc] init];
UIView *tempview = [[UIView alloc] initWithFrame:CGRectMake(15, 30, 320, 460)];
[array addObject:tempView];
[tempview release];
UIView *tempview2 = [[UIView alloc] initWithFrame:CGRectMake(15, 30, 320, 460)];
[array addObject:tempView2];
[tempview2 release];
[array release];

Memory leak with array

Can someone point me in the right direction. When I load this file in as a nib, and unload it, and reload it instruments says I'm getting a memory leak. Specifically, it's says that where I set the compareOptions NSMutableArray, and where I call [vc release].
CompareOptions is a synthesize property that is also released in the dealloc.
Many thanks in advance.
- (void)viewDidLoad{
[super viewDidLoad];
//NSLog(#"Comparison.viewDidLoad");
self.compareOptions = [[NSMutableArray alloc] init];
self.tabs = [[ComparisonTabs alloc] initWithFrame:CGRectMake(450, 85, 650, 50)];
//NSDictionary * currComparison = (NSDictionary*)[data objectAtIndex:0];
//NSArray * correctOptions = [currComparison objectForKey:#"correct"];
for(int i = 0; i < 3; i++)
{
UIViewController * vc = [[UIViewController alloc] initWithNibName:#"ComparisonOptions" bundle:nil];
ComparisonOptions * options = (ComparisonOptions *)vc.view;
[options setup];
options.index = i;
//options.frame = CGRectMake(355 + (306 * i), 475, options.frame.size.width, options.frame.size.height);
//[options setCorrect:[correctOptions objectAtIndex:i]];
[vc release];
[self.view addSubview:options];
[self.compareOptions addObject:options];
}
[self.view addSubview:self.tabs];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(tabSelectedHandler:) name:#"tabSelected" object:nil ];
[self update:0];}
how does the property of compareOptions and tabs look like? Does it retain your objects?
If it does retain, then you'll get a double retain if you use the setter and alloc.
self.compareOptions = [[NSMutableArray alloc] init];
^ retains ^^^^^ retains
self.tabs = [[ComparisonTabs alloc] initWithFrame:CGRectMake(450, 85, 650, 50)];
^ retains ^^^^^ retains
you could use this instead
self.compareOptions = [NSMutableArray array];
self.tabs = [[[ComparisonTabs alloc] initWithFrame:CGRectMake(450, 85, 650, 50)] autorelease];
UIViewController * vc = [[UIViewController alloc] initWithNibName:#"ComparisonOptions" bundle:nil];
ComparisonOptions * options = (ComparisonOptions *)vc.view;
[vc release];
[self.view addSubview:options];
vc.view (ie options) will be deallocated at the same time vc will be deallocated. And this happens when you call [vc release]. You can't use options after this.
You should release vc after you've added the view to the subview.
And you should think about better class names. I would never assume that ComparisonOptions is a View. It sounds more like NSCaseInsensitiveSearch etc. You know, like it would be an option.

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.