For a view controller, any outlets that you set in Interface Builder must be released
and set to nil in viewDidUnload, and must also be released in dealloc.
(see: When should I release objects in viewDidUnload rather than in dealloc?)
One of the most important reasons for implementing [viewDidUnload] is that UIViewController subclasses commonly also contain owning references to various subviews in the view hierarchy. These properties could have been set through IBOutlets when loading from a nib, or programmatically inside loadView [emphasis added], for instance.
My question is, do we really need to implement viewDidUnload for subviews in the view hierarchy that are created programmatically in loadView (without Interface Builder)?
It depends how you created them and if you need to reference them elsewhere.
For example:
- (void)loadView
{
[super loadView];
UIButton *someButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
someButton.frame = CGRectMake(0, 0, 50, 50);
[self.view addSubview: someButton];
}
In the above case you would not need to implement viewDidUnload because someButton is autoreleased within loadView.
Another example:
- (void)loadView
{
[super loadView];
self.someButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
someButton.frame = CGRectMake(0, 0, 50, 50);
[self.view addSubview: someButton];
}
In this example you would want to use viewDidUnload, as you have another reference to someButton hanging around. You want viewDidUnload to release that button and reset the reference so you don't improperly use it as well as free up the memory. In this case you would also want to release the button in the dealloc method as well, in case viewDidUnload is never called.
You certainly should. If you look through Apple samples, however, you'll find that sometimes they just use dealloc. If you know your object will get deallocated at some reasonable time after use, I think this is perfectly reasonable. I follow this pattern, however, as viewDidUnload might not be called in certain exceptional cases. I do not actually call the release method such a long name:
-(void)releaseRetainedXibAndViewDidLoadObjects
{
self.myLabel = nil;
self.myImage = nil;
}
-(void)viewDidUnload
{
[super viewDidUnload];
[self releaseRetainedXibAndViewDidLoadObjects];
}
-(void)dealloc
{
self.myObject = nil;
[self releaseRetainedXibAndViewDidLoadObjects];
[super dealloc];
}
You could even do this from an application specific view controller object that your classes subclass to simplify matters.
Do it in your dealloc method. There is no guarantee that viewDidUnload will be invoked. It is typically only invoked when the controller needs to unload the view, e.g. when there's a memory warning. On the other hand, the init/dealloc pairing will always be invoked.
Related
I dynamically add a subview to another uiview;
in the subview ,click a button to go back by the following method:
[self removeFromSuperView ];
but after manny times added subview and removed it,my app could crash ,the log said it was killed .
I found that calling [self removeFromSuperView] didn't release self. So what's the best methods to releas it?
If you are retaining the UIView on creation (or adding it to an array) the retain count will increase. For example:
// Retain count is 1
UIView *myView = [[UIView alloc] initWithFrame:myFrame];
// Retain count is 2
[myParentView addSubview:myView];
// Retain count is 1 again
[myView removeFromSuperView];
In the above example you can autorelease the view if it is immediately added as a subView or release it in your dealloc if it is an iVar.
EDIT: (other reasons your view could be retained)
// Retain count +1
[myArray addObject:myView];
// Retained in the setter created by the #synthesize directive
#property(nonatomic, retain) UIView *myView;
Anything else that states in the documentation that the property is retained.
You should also be careful of creating objects in the loadView method of a VC, if you do make sure you release them, as they will be created again when the loadView is called. This will happen if you VC's view is unloaded and then reloaded.
u should release at first. counterpart of "alloc" is "release", and counterpart of "addSubview" is "removeFromSuperView":keep those balance.
add view:
UIView *myView = [[UIView alloc] initWithFrame:myFrame];
[myParentView addSubview:myView];
[myView release];
remove view (the view will clear up in memory after removeFromSuperView):
[myView removeFromSuperView];
Looks like you are adding retained view as a subview. Its parent view retains it once again.
So when you cell [self removeFromSuperView]; it gets release message from superView, but still have to be releasd by creator.
I am creating a Table View in viewDidAppear, because my app requires it to be created here for multiple reasons. However I notice that I get a memory leak when I analyze my application.
I thought using the instance variable _tableView is not a good idea in any other method besides init and dealloc. Should I just use autorelease, I want to make sure the table gets released at the appropriate time.
There is a property for my Table View.
#property (nonatomic, retain) UITableView *tableView;
And I create the Table View as such:
- (void)viewDidAppear:(BOOL)animated
{
self.tableView = [[UITableView alloc]
initWithFrame:CGRectMake(0, 0, 320, 300)
style:UITableViewStyleGrouped];
// Table View properties
self.tableView.dataSource = self;
self.tableView.delegate = self;
[self.view addSubview:self.tableView];
}
- (void)viewDidDisappear:(BOOL)animated
{
self.tableView = nil;
}
- (void)dealloc
{
[_tableView release];
}
self.tableView is a retain property, so your synthesized setter increases the retain count. But when you create a new UITableView using alloc/init, you also increase the retain count. So this line results in tableView being retained twice:
self.tableView = [[UITableView alloc]
initWithFrame:CGRectMake(0, 0, 320, 300)
style:UITableViewStyleGrouped];
Once when you use alloc/init, and once when you call the synthesized setter using self.tableView =.
You do not have two corresponding release calls.
The proper way of handling this would be to autorelease the alloc/init'd UITableView object that you set self.tableView to, like so:
self.tableView = [[[UITableView alloc]
initWithFrame:CGRectMake(0, 0, 320, 300)
style:UITableViewStyleGrouped]
autorelease];
The rest of your code will work as expected.
As an aside, you probably don't want to create your UITableView in viewDidAppear. By that time your view has already appeared (hence the name), and you likely wanted your UITableView before that. You also probably don't want the CPU expense of creating a new UITableView every time the view appears. You probably want to create a UITableView in viewDidLoad and then reuse it, unless there is a really good reason not to.
Autorelease. You get one retain for the alloc and another for the property assignment (assuming it's a retained property).
Your dealloc should handle one and an autorelease the other.
Setting property is good, i set the property for instance variable when you want to use that variable in another class, otherwise it is not necessary ,
Since you are creating in viewDidAppear it will create always when the view appears for that controller so better relesase the tableview in ViewDidDisappear method.
If you use autorelease we wont get when it will be released so some times accessing tableview will cause crash of application,
On top of what the other guys have said, I'd discourage you from doing alloc in viewDidAppear because that method can be called multiple times depending upon what else goes on (and, by the way, cause leaks unless you start checking for the existence of a tableView before alloc'ing another). Doing it in viewDidLoad seems to be much safer.
As an aside, I believe that your viewDidAppear, viewDidDisappear and dealloc should all be calling their super versions, too.
Should I release my subviews of UIView in the viewDidUnload when I have references to them as instance variables which retains them? I have build the GUI programmatically. I should do that right? Since both uiview and ivars retain then the objects would have 2 in retain-count, when view receives e.g. memory-warning then the UIView will release the subviews, but they still have +1 in retain count so I have to setself.myIvar = nil; In the viewDidUnload?
Thanks for your time.
You actually can release all retained subviews in viewDidUnload. But I used to do it in another way:
-(void) viewDidLoad {
someInstanceView1 = [[UIView alloc] init];
[self.view addSubview: someInstanceView1];
[someInstanceView1 release];
someInstanceView2 = [[UIView alloc] init];
[self.view addSubview: someInstanceView2];
[someInstanceView2 release];
//etc...
//you have a references to someInstanceView1 and someInstanceView2 with retained counts 1
}
In this case even if memory warning will arise, the view controller will remove all it's view subviews. And then call viewDidLoad again. So there would be no leaks and you don't need to care about releasing that ivars at all cause the only owner (it has the strong reference to the views) is the view controller's view and it will release them automatically.
I have created a view and assigned the view to viewcontroller
UIView *newView=[[UIView alloc] initWithFrame:CGRectMake(0,0,320,460)];
in viewDidLoad method I have assigned the view to the viewcontroller
-(void)viewDidLoad{
self.view=newView;
//[view release]; In this case also the application crashing
}
-(void)dealloc{
[newView release];//IN this case also the application crashing.
[super dealloc];
}
The crash log is this.
how to release the newView? or else the viewcontroller itself will take care of releasing the newView.
In most circumstances you will do the following:
- (void) loadView {
// Don't call super here if you assign the view property
UIView *newView = [[UIView alloc] initWithFrame: [UIScreen mainScreen].applicationFrame];
self.view = newView;
[newView release];
// the view is now owned and retained by the UIViewController
}
Apple recommends you allocate the view, assign and release it in -loadView and don't worry about it in -dealloc. This is important in cases when your view may be purged under low memory conditions.
Somewhere I was reading that I would run into memory problems when I give up a view temporary due to an low memory warning (loading it again as soon as the user wants to see it), if theViewController class does not do things like this on every outlet of that view:
-(void)dealloc {
[myView release], myView = nil;
[myLabel release], myLabel = nil;
[super dealloc];
}
I am confused here, because actually I thought that [myView release] would also make the nil-thing, saying that variable holds no object anymore. Why twice? Or is there something I missed?
Calling [myView release] doesn't change the value of myView, it decrements the retain count of the object that myView points to (and when the retain count of an object goes to zero, it deallocs itself). After calling [myView release], myView still contains the address of the view object, but if myView was the only owner, that object has dealloced itself and the memory is no longer valid (and my now be occupied by another object or other data). By setting myView to nil, you make sure that you don't accidently use the old memory that used to belong to myView, and you can now test if myView has been initialized or discarded.
Note that you don't need to set myView to nil in your -dealloc method, since your view controller is going away at this point anyway, but you would want to do this if you discard your view in response to a low memory warning. You might also consider discarding the whole view controller if its view isn't visible.
When a view controller gets a memory warning, and the view is off-screen, it may set the view property to nil. But that won't release any subviews that you might be retaining in other properties. So here's how you can handle that:
- (void)setView:(UIView *)view
{
[super setView:view];
if (view == nil)
{
// Release-and-nil any subviews that you might be retaining.
}
}