views:

1618

answers:

6

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?

+8  A: 

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.

Daniel Dickison
A: 

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.

Marc Charbonneau
A: 

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

A: 

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

nessence
Then I am wondering why would you have an implementation denote the imgView in the .h file at all? I am seriously asking cos I don't get it. If you release right after you are done assigning the .image property, why not instantiate the imgView right before you use it?
Jann
You're correct. For OP, it's probably not necessary. Is the image used elsewhere? Then it's probably necessary.
nessence
A: 

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];
}
Tobias Cohen
imgView is not released too early. It's being retained by addSubview. This is a very common idiom to release right after adding via addSubview (or any other calls that retain, such as UINavigationController's pushViewController.
Boon
A slightly simpler method is to assign the ivar (imgView) directly instead of using self.imgView later. This eliminates the need for [newImgView release] later in the code.
Sophtware
@boon Oops, you're quite right - I think I must have misread the original code. At any rate, storing something in an instance variable after you have released it is a mistake, you could end up writing code that sends a message to the object after it has been deallocated (although I admit that's very unlikely in this specific case).
Tobias Cohen
A: 

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

Elliot
I think I was confused. I deleted my answer as it wasn't helping. Thanks for the feedback.
bentford