UIViewControllers... Memory Management With SubViews - iphone

I have UIViewController who has several subviews but tracking each and every subview allocated is difficult task as the actually coder is not me and need to handle the memory consumption.
My question is there a way to control the memory calling a recursive function to remove and release all the subViews in the UIViewController without knowing the actual reference name ?
As in the code below :
for (UIView* subview in view){
[subview removeFromSuperView];
[subview release] ;
subview = nil;
}

When you call removeFromSuperview on a view it will automatically decrement the retain count (because the superview no longer requires a reference to the view you've just removed).
If you have added your views to the superview in a standard manner there shouldn't be any need to do what you're doing - either you've added your views and then released them, or your views are properties and the release happens later.
The code you're proposing (the recursive loop on all subviews) is a bad idea, because you don't actually know whether your subview is safe to be released or not. You could easily trigger a bad access.

Related

Arc: Setting references to nil, for multiple buttons

When displaying an item such as a button in a ViewController, I declare it in the header file:
#property (nonatomic, strong) UIButton *startButton;
Synthesize it in the implementation file:
#synthesize startButton;
Initiate it in the implementation file:
startButton = [[UIButton alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
And set the reference to nil in viewDidUnload:
[self setStartButton:nil];
However, what if I have 60 listed items (in a UIScrollView) which are created dynamically in a loop, in viewDidLoad (fed from Core Data)?
(Not using TableView, because the items are quite complicated and interactive.)
Should I declare the items anywhere first? Should I set them to nil anywhere? (Or should I just keep my fingers crossed and pray to the Arc?)
Some guidance would be appreciated. Thanks.
Does your view controller have properties or instance variables that point to each of those 60 items?
Yes: Then set those ivars or properties to nil in your -viewDidUnload.
No: Then there's nothing to worry about.
ARC changes what you do to manage memory, but it doesn't change the basic philosophy, which is that every object should take care of its own references to other objects but not worry about anybody else's.
In this case, the view that contains all those items will have strong references to the items (via it's array of subviews). When the view controller releases its view, that view will then be deallocated and release all its subviews in the process. So all those items will be taken care of. If you did have strong references to any of those items, those references will prevent the referenced objects from being deallocated -- setting the references to nil will avoid that.
If you are creating these views on the fly and adding them as subviews of the scrollView, then you don't need to do anything else.
When the scrollView is released during -viewDidUnload, it will properly clean up all its subviews.
Update
Think of it this way. In a normal view setup on a storyboard, you don't need have a property for every view, only the views your controller interacts with. This is basically the same thing. You have a bunch of views that are embedded into a superview. If the controller doesn't interact with them, then you don't need to worry about them.
no. :)
you definitely set the all pointer to nil what are not necessary anymore when you are using the ARC.
the ARC will release the memory of the current object when there is no more strong pointer who tries to keep alive the object. if you don't set them to nil the ARC thinks you are still using them and they will be in the memory and they will cause serious memory leak.
UPDATE #1
if you have any reference for the buttons this is the easiest way to do it, you don't need to do set to nil anything, because their superview class retain them and you should just remove from their superview.
for (UIView *_subView in self.view.subviews) {
if ([_subView isKindOfClass:[UIButton class]]) {
[_subView removeFromSuperview];
}
}
if you're storing their pointer any other place (in an NSArray for ex.) you must have to remove the pointers from them as well.

UIView Delete Itself?

I have a UIView subclass called ToolbarView that sort of acts like a floating window and has a UIToolbar that has an "X" button that can make the window disappear. My question is, is it possible for the object to delete itself from within its own class?
For instance, within ToolbarView.m, I have a method closeButtonPushed which fires when the X button is pushed. Is it as simple as to remove the view from its superview, and then call dealloc? Is it even necessary to call dealloc as once it is removed from the superview there won't be any other pointers to it. Or is this bad memory practice?
Yeah, don't call dealloc ever. If the superview is the only object that has a reference to your view, removing the view will cause its retain count to go to 0, and the runtime will then deallocate the object for you.
What you have to do is in closeButtonPushed method is, call
[self removeFromSuperView];
it will remove the ToolbarView object and IOS will call the dealloc method and release objects. No need to call dealloc manually.

(iphone) uiimage, view, subviews memory management

I need to release uiimage/view/subviews when I want, and have a few questions regarding proper practice of releasing them.
[imageView removeFromSuperview] would release the imageView and imageView.image?
view = nil; would release its subviews/and associated uiimages recursively? if not, should I implement a recursive function to release a view's subviews?
Thank you
Edit.
I looked at UIView's library reference
addSubview --
This method retains view and sets its
next responder to the receiver, which
is its new superview.
removeFromSuperview --
If the receiver’s superview is not
nil, the superview releases the
receiver. If you plan to reuse a view,
be sure to retain it before calling
this method and release it again later
as appropriate.
still not sure [imageView release] releases uiImage associated with it,
and would I still need recursive releasing of subviews. ie a view's getting dealloced would automatically guarantee it's subviews release?
[imageView removeFromSuperview] would
release the imageView and
imageView.image?
removeSuperView calls release on the view, but you should pay attention to the views retain count. Just because you called removeFromSuperview doesn't mean it's doing what you want.
view = nil; would release its
subviews/and associated uiimages
recursively? if not, should I
implement a recursive function to
release a view's subviews?
No, you probably want to do (depending on how you've managed your subviews during their creation. If the superview was their only reference, they likely have a retain count of 1 and therefore calling release after calling removeFromSuperview will result in an error):
for (UIView* subview in view){
[subview removeFromSuperView];
[subview release]
}
[view release];
EDIT: To answer your final question, no, calling release on a view does not automatically call release on all of its subviews. You have to do it yourself, whether with release or with removeFromSuperview.
When you do [imageView removeFromSuperview], it won't release anything. You need to so [imageView release] afterward. Even so, you still need to put your memory releasing for that view in imageView's dealloc.

Does UIView's removeFromSuperView method remove the UIView from memory

Does UIView's removeFromSuperView method remove the UIView from memory? I just want to be sure that the view is no longer using any memory?
UIView retains its subviews, so when you call -removeFromSuperview method then your view object is released.
So if everything else is ok with your memory management and your view is not retained by anything else then yes - your view should be deallocated and removed from memory.
Possible example when simply removing view from superview may not be sufficient to deallocate it can be view that has outlet connection and declared property for it with retain attribute - in that case it is retained by controller when it is being loaded from nib file and you may need to release that view for that case:
[iVarView removeFromSuperview];
self.iVarView = nil;
Yes. Once removeFromSuperView method is called, that view is also released from memory.
If you want to confirm with Apple's documentation, here is the link.

When should I release objects in -(void)viewDidUnload rather than in -dealloc?

What is the -(void)viewDidUnload is good for?
Could I not just relase everything in -dealloc? If the view did unload, wouldn't -dealloc be called anyway?
In addition to what has already been indicated, I wanted to elaborate more about logic behind -viewDidUnload.
One of the most important reasons for implementing it 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, for instance.
The additional ownership of subviews by the UIViewController means that even when its view is removed from the view hierarchy and released to save memory, through which the subviews are also released by the view, they will not actually be deallocated because the UIViewController itself still contains its own outstanding retaining references to those objects as well. Releasing the UIViewController additional ownership of these objects ensures they will be deallocated as well to free memory.
The objects that you release here are usually recreated and set again when the UIViewController view is re-loaded, either from a Nib or through an implementation of -loadView.
Also note that the UIViewController view property is nil by the time this method is called.
As the documentation says:
It is called during low-memory conditions when the view controller needs to release its view and any objects associated with that view to free up memory.
In the same situation dealloc is not called. This method is only available in OS3 and above. Dealing with the same situation in iPhone OS 2.x was a real pain!
Update July 2015: It should be noted that viewDidUnload was deprecated in iOS 6 because "Views are no longer purged under low-memory conditions and so this method is never called." So, the modern advice is not to worry about it and use dealloc.
This is because you will typically set the #property as "(nonatomic, retain)" and as such the setter that is created for you releases the current object and then retains the argument i.e.
self.property = nil;
...does something along the lines of:
[property release];
property = [nil retain];
Therefore you are killing two birds with one stone: memory management (releasing the existing object) and assigning the pointer to nil (since sending any message to a nil pointer will return nil).
Hope that helps.
Remember that viewDidUnload is a method in the view controller, not in the view. The view's dealloc method will get called when the view unloads, but the view controller's dealloc method may not be called until later.
If you get a low memory warning and your view isn't showing, which will happen for instance about any time you use a UIImagePickerController to let the user take a picture, your view will get unloaded and will need to get reloaded after that.
Conclusion:
View Controllers have a view property. Typically a nib or piece of code adds other views to this view. This happens often inside a -viewDidLoad method, like this:
- (void)viewDidLoad {
[super viewDidLoad];
[self createManyViewsAndAddThemToSelfDotView];
}
in addition, a nib file may create a button and append it to the view controller's view.
On iPhone OS 2.2, when -didReceiveMemoryWarning was invoked from the system, you had to release something to free up memory. You could release the whole view controller's view if that made sense. Or just big memory-consuming contents in it.
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
// Release anything that's not essential, such as cached data
}
Now, in the new OS 3.0, there is an -viewDidUnload method, which will be invoked from the system when the view has been unloaded because of low memory (please correct me: when exactly does this get called?)
-viewDidUnload is used to release all objects that were owned both by the view controller itself and the view. The reason: If a view controller holds references to childs of the view, i.e. a button, the referenced child views will not get released, because their retain count is >= 1. After they are released in -viewDidUnload, they can get freed up from memory.
Apple deprecated viewWillUnload, now you shoud use didReceiveMemoryWarning or dealloc to release your objetcs.
In iOS 6, the viewWillUnload and viewDidUnload methods of
UIViewController are now deprecated. If you were using these methods
to release data, use the didReceiveMemoryWarning method instead. You
can also use this method to release references to the view
controller’s view if it is not being used. You would need to test that
the view is not in a window before doing this.
If the view controller is popped from the navigation controller stack and is not retained anywhere else, it will be deallocated, and dealloc will be called instead of viewDidUnload. You should release the views created in loadView in dealloc, but it is not necessary to set the variables to nil, because soon after dealloc is called the variables will no longer exist.
You can release any subviews you hold on to, for example that UIImageView you retained in your loadView method, or better yet the image that was on that UIImageView.