UIView and memory management - iphone

A beginners question about how to be memory efficient when using an UIView which contains a couple of images (ca. 500K). I guess if I handle this in the wrong way and call this view ten or twenty times, my app will crash (as I have leaked about 5-10 MB of RAM).
I have an UIView which I create programatically like so:
myView = [[UIView alloc] initWithFrame:0,0,0,0];
To this view I add a couple of images so that it eats up 500K of memory. After I'm done with this view, I'd like to free up the memory again. So I coded:
[myView removeFromSuperview];
myView = nil;
[myView release];
Is this the way to go? I am particularly uncertain about the last release call. Is myView not already released if I remove it from my superview and set it to nil?
Also, would it be a good idea to simply autorelease myView in the first instance, i.e.
myView = [[[UIView alloc] initWithFrame:0,0,0,0] autorelease];
I'd be grateful for any suggestions and corrections.

You’re sending a release message to nil. The correct order for those statements would be:
[myView removeFromSuperview];
[myView release];
and optionally after that:
myView = nil;
For discussion on why to set to nil:
Set pointers to nil after release?
Is It Necessary to Set Pointers to nil in Objective-C After release?
What's the difference between setting an object to nil vs. sending it a release message in dealloc
The superview retains your view when you add it as a subview, and then releases it when you remove it. You still need you release your hold of it. You could use autorelease when allocating it, but since you need to hold on to a pointer to it to be able to send removeFromSuperview, the correct way is to send release when you are done with that pointer (and then set that pointer to nil).

If you set your view to nil before you call release, you will leak the view and then send a message to nil. First you must release the view:
[myView removeFromSuperview];
[myView release];
Then you can set your variable to nil to avoid sending a message to a deallocated instance.

About the autorelease, I think it's just a matter of personal preference, but I find it much easier to track memory issues when doing:
myView = [[[UIView alloc] initWithFrame:0,0,0,0] autorelease];
// add myView to wherever it belongs
.....
[myView removeFromSuperview];
myView = nil;
As others have pointed out, settting to myView to nil before you call release is incorrect and will leak memory.

Related

is removeFromSuperview releases the object?

I am using removeFromSuperview for removing a view from its superview. I am also using release after removeFromSuperview on that object, sometimes its work fine but sometimes give bad access.
is removeFromSuperview also call release to the object ?
Yes, removeFromSuperview also releases view.
You need to release your view after you have added it to its superview.
CGRect frame; // let's assume you already have defined frame and superview
UIView *superview;
UIView *subview = [[UIView alloc] initWithFrame:frame];
[superview addSubview:subview];
[subview release];
Or just use autorelease, when you create a view.
UIView *subview = [[[UIView alloc] initWithFrame:frame] autorelease];
[superview addSubview:subview];
In both cases, you don't need to call release after you call removeFromSuperview.
Yes, addSubview calls retain, and removeFromSuperview calls release. So your call to release does not belong there (it probably belongs after you assign the subview, assuming you do not want to hold the object).
The correct answer is "probably". This is an implementation detail that you shouldn't even really be thinking about. If your code needs to work with the subview, then you should be retaining it before you start working on it, and you should be releasing it when you're finished working with it (which may or may not coincide with removing it from it's superview).
I imagine testing would show that removeFromSuperview does release the view, but you cannot depend on this behaviour and should never assume it will happen. Even if it does always happen now it might not in future.
You shouldn't even need to think about memory management at such a low level as this. If you retain the object with retain or copy or alloc or new then you are responsible for counter-acting that with a release at some point in the future.
If you did not do any of these things, then you should not be releasing it. The methods for adding a subview to a view do not contain retain or copy in their name and therefore you are not supposed to release it yourself when you remove it from it's superview.
The view system is very complicated, especially on an iPhone with such a limited amount of RAM and not inadequate storage to use virtual memory. Stick to the rules and you should be fine. Or enable ARC.
Actually removeFromSuperview decreases the retainCount property of the view Object and so as the release.
So,if you do the way Aleksejs said.
UIView *subview = [[UIView alloc] initWithFrame:frame];
[superview addSubview:subview];
[subview release];
subview -> alloc= retainCount = +1 ,
subview -> addSubView = retainCount = +1
subview -> release = retainCount = -1.
subview -> removeFromSuperView = retainCount = -1
(subview removed from memory)

how to release UIVIewController after remove his view

How to release view controller created like this:
VCClass *vc = [[VCClass alloc] initWithNibName:#"VCClass" bundle:nil];
[self.view addSubview:vc.view];
so the view appear, UIViewController is allocated. Now I want to releas it from within VCClass. I call inside VCClass:
[self.view removeFromSuperView];
my question is, where should I release "vc" object attached to removed view. Is there a good way to notify viewcontroller that is can be released while view is released ?
addSubview does a +1 to the retain count, and it's usually a good practice to release as soon as you don't need it, and you're handing it to another pointer. It's like a glass ball, it is passed hand by hand, and if no one is holding, it falls to the ground and breaks.
Example:
UIView *sampleView = [[UIView alloc] init]; // Retain count: 1
[self.view addSubview:sampleView]; // Retain count: 2
[self.view release]; // Retain count: 1
When the removeFromSubview: is called, the object will be released:
[sampleView removeFromSuperView]; // Retain count: 0
That's for memory management.
Answering your question, a safer way to do what you want to do (loading just a part of an ViewController from a nib (I'm assuming you're using a nib, because you used #"VCClass" in the initWithNibName:), is to use it as following:
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"VCClass" owner:self options:nil];
UIView *view = (UIView*)[nib objectAtIndex:0];
This works by loading the NibName into memory, and then stealing the first element (if you only have a UIView inside, then it will pick that, as the top-most element). This is done similarly for UITableViewCells when loading them from nib files. Nib Files are autoreleased, and it makes more sense, since you apparently just care about the view itself, not the controller.
After you remove it, add a call to
[self autorelease];
Views don't know about their view controllers except as a weak reference to a delegate. This is to avoid a circular reference, among other reasons. VCs often have a life outside their views - hence, the viewDidLoad and viewDidUnload messages. For example, throughout the lifetime of a tab-bar application, the VCs for each tab might go through many different view instances while never being deallocated. Therefore, you should avoid having the view release its own view controller.
Often, the class that allocated the VC should be the one to release it. In the code you provided, you have:
VCClass *vc = [[VCClass alloc] initWithNibName:#"VCClass" bundle:nil];
[self.view addSubview:vc.view];
The controller class that the above code is in is probably the place best suited to releasing the VC. You might need to devise a delegate call just for this purpose.
[self.view removeFromSuperView]; should release your said view from the memory. Though be warned that this will not be true if your view has been retained by any other object that is its retain count is more than 1. Also look at the second answer on this thread.
Does UIView's removeFromSuperView method remove the UIView from memory

OpenFlow crashes app whem trying to reset/ recreate

I am trying desperately to change the images on an OpenFlow instance with no luck.
I am kind of giving up so I am trying now to remove the instance and create a new one. But I can't avoid crashing the app.
The code to create it is:
AFOpenFlowView *of = [[AFOpenFlowView alloc] initWithFrame:CGRectMake(0, 100, 320, 380)];
[of setCenter:CGPointMake(160, 240)];
[of setBackgroundColor:[UIColor blackColor]];
[of setDataSource:self];
[of setViewDelegate:self];
[self setPeopleFlow:of];
[self.view addSubview:peopleFlow];
[of release];
Then, on a click of a button I do:
[peopleFlow removeFromSuperview];
[peopleFlow release];
Later on I call the same function with the first block of code to create it again and it's when the application crashes with no log error.
Any ideas on how to clean the OpenFlow object to repopulate it without having to remove/recreate? Or how create/recreate reliably?
When you create the peopleFlow instance, it has a retain count of 1.
Then when you add it as a subview, the super view retains it, so it's retain count is 2.
Then you release it after adding it to the superview, so it's retain count is 1 again.
Then You remove it from the superview, and the superview releases it, so it's retain count is 0 and the object gets deallocated.
Then you try and release it again, and it crashes because you're sending release to a deallocated object.
Long story short, in this case, you don't need to release it after removing it from the superview.
Also, it is good practise to assign nil to a pointer if you release it and don't care about what is pointing at any more. This is because after you release and deallocate an object, the pointer variable is still pointing to the memory that the object used to occupy. Assigning nil to the pointer prevents any bad stuff happening if you try to send a message to whatever the dangling pointer is pointing to.

Release for controllers and views

If I have a set of custom UIViewControllers that I release in a high level application "restart" routine, would a good way to release their views be to set
self.view = nil;
in the dealloc method?
I'm not sure where your views are, but you usually would want to remove them from superview (if they have one)
[someView removeFromSuperview];
if it's retained by something else other than its superview, you'd want to release it
[someView release];
assuming your retainCount is then 0, dealloc will be called (in 99% of the cases, you should never call dealloc yourself)
then yes, you would want to nil it.
someView = nil;
then you can recreate your views or whatever you want to do.

iPhone Memory Management and Releasing

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?
The code is incorrect. You'll end up releasing imgView after it's been deallocated.
In your .m file, you:
alloc it --> you own it
add it as a subview --> you and the UIView owns it
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
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];
}