Question

Here's a common practice I see often (including from a very popular iPhone developer book)

In the .h file:

@interface SomeViewController : UIViewController
{
  UIImageView *imgView;
}

Somewhere in the .m file:

imgView = [[UIImageView alloc] initWithFrame:[[UIScreen mainScreen]
applicationFrame]];
[imgView setImage:[UIImage imageNamed:@"someimage.png"]];
[self addSubview:imgView];
[imgView release];

And later, we see this...

- (void) dealloc
{
  [imgView release];
  [super dealloc];

} 

Since imgView has a matching alloc and release, is the release of imgView in dealloc necessary?

Where is the imgView retained by the addSubview call accounted for?

Was it helpful?

Solution

Yes, that code has problems. It releases the imgView too early which could potentially cause crashes in rare circumstances stores an object in an instance variable without retaining it, and it's just generally going about memory management the wrong way.

One correct way to do this would be:

@interface SomeViewController : UIViewController
{
    UIImageView *imgView;
}
@property (nonatomic, retain) UIImageView *imgView;

And in the implementation;

@synthesize imgView;

Somewhere in the module:

//Create a new image view object and store it in a local variable (retain count 1)
UIImageView *newImgView = [[UIImageView alloc] initWithFrame:self.view.bounds];
newImgView.image = [UIImage imageNamed:@"someimage.png"];

//Use our property to store our new image view as an instance variable,
//if an old value of imgView exists, it will be released by generated method,
//and our newImgView gets retained (retain count 2)
self.imgView = newImgView;

//Release local variable, since the new UIImageView is safely stored in the
//imgView instance variable. (retain count 1)
[newImgView release];

//Add the new imgView to main view, it's retain count will be incremented,
//and the UIImageView will remain in memory until it is released by both the
//main view and this controller. (retain count 2)
[self.view addSubview:self.imgView];

And the dealloc remains the same:

- (void) dealloc
{
    [imgView release];
    [super dealloc];
}

OTHER TIPS

The code is incorrect. You'll end up releasing imgView after it's been deallocated.

In your .m file, you:

  1. alloc it --> you own it
  2. add it as a subview --> you and the UIView owns it
  3. release it --> you don't own it

Then in dealloc, you release imgView even though, as we established at step 3 above, you don't own it. When you call [super dealloc], the view will release all of its subviews, and I imagine you'll get an exception.

If you want to keep an ivar of imgView, I suggest not calling release after you add it as a subview, and keep your dealloc the same. That way, even if imgView is at some point removed from the view hierarchy, you'll still have a valid reference to it.

The code is incorrect, you shouldn't be releasing it in the init method, just when dealloc is called (that's if you want to keep it as an ivar, you don't need to unless you need a pointer to it elsewhere since addSubview: will retain the view for you).

I believe the reason it's not actually crashing is because it's still being retained by the superclass (from the call to addSubview:), so when it's released in dealloc that's actually balanced out. The view probably removes itself from the superview when it's deallocated immediately afterwards, so when [super dealloc] is called it's not being over-released. That's my hunch, at lease.

Release in init is incorrect.

You mentioned "common practice" and an un-named book. I suggest looking at the canonical examples from Apple: ViewTransitions is a good example for this case (and 2 views to boot ;)

http://developer.apple.com/iphone/library/samplecode/ViewTransitions/index.html

(I don't have enough reputation to add comment yet.)

@bentford: Correct me if I'm wrong, but I believe that in order to use the imgView property's synthesized setter, you must use "self.imgView":

self.imgView = [[UIImageView alloc] initWithFrame:[[UIScreen mainScreen]

If you don't have self., it's just using the ivar, and it's not getting the additional retain.

The basic answer is, there should only be one [imgView release] in the example code (whether it's after addSubview or in dealloc). However, I would remove [imgView release] from dealloc and leave it after addSubview.

There is a catch on the iPhone; with didReceiveMemoryWarning, you could have objects (including an entire view) released out from under you. If you have an application-wide retain set and you don't respect memory then you could find the application simply being killed.

A good example is:
if you think of a nested set of 3 views, View 1-> View 2-> View 3. Next, consider the 'viewDidLoad' and 'viewDidUnload' calls. If the user is currently in 'View 3', it's possible that View1 is unloaded, and this is where it gets nasty.
If you allocated an object inside viewDidLoad and didn't release it after adding it to the subview, then your object isn't released when view1 is unloaded, but, view1 is still unloaded.
viewDidLoad will run again and your code will run again, but now you've got two instantiations of your object instead of one; one object will be in nowhereland with the previously-unloaded view and the new object will be for the currently visible view. Rinse, lather, and repeat and you find your application crashing from memory leaks.

In this example, if the given block of code is volatile and has a chance to be executed again (whether because of memory or an unloaded view), I would remove [imgView release]; from dealloc and leave it after addSubView.

Here is a link on basic retain/release concepts: http://www.otierney.net/objective-c.html#retain

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top