Do I need to call release here or not?
I am Loading view in didSelectRowAtIndexPath in tableview...
EventDetailedViewController *eventDetailedViewController=[[EventDetailedViewController alloc]initWithNibName:#"EventDetailedViewController" bundle:nil];
eventDetailedViewController.aEventInfo=aEventInfo;
[self.navigationController pushViewController:eventDetailedViewController animated:YES];
// [eventDetailedViewController release];
eventDetailedViewController=nil;
When I need to call [eventDetailedViewController release]; and when I need not to call [eventDetailedViewController release]. didSelectRowAtIndexPath in tableview...
EDIT:
I do have three views when I select it, it loads first nib. From first it loads second nib file. From second it loads third nib file. When I get back from third to second to first, my application crashes... I am thinking it's due to releasing my first view controller.
Apple's memory management rules are very simple: every call to alloc/new/copy*/retain must be balanced by a call to auto-/release. Anything (object or code block) that calls the former has ownership; when the owner no longer needs the owned object, release it. self.navigationController, for example, will retain a pushed view controller (asserting ownership) until the controller is popped form the navigation controller's stack. The only thing this simple(-istic?) explanation leaves out is when to use weak references (necessary to prevent retain cycles).
Since eventDetailedViewController is a local variable, it's the code block that owns eventDetailedViewController. Since the variable isn't static, the variable will be discarded when the block exits and the block can't make use of the object after that, so the object needs to be released.
Since you're are not going to use eventDetailedViewController (you're setting it to nil), you should release it. The UINavigationController's pushViewController method will retain the object so it won't be released until the navigationController no longer needs it.
Setting it to nil, I believe, is only useful if it is a property that's nonatomic where setting it to nil will release the previous value. Since it's a local variable, you should release it.
Related
I've been facing a weird issue with dismissing a modal view.
I present a modal view like this:
ResepiDetail *detail =(ResepiDetail*)[[ResepiDetail alloc]init];
[self presentModalViewController:detail animated:YES];
and dismiss it like this with a back button:
[self dismissModalViewControllerAnimated:YES];
after this the view dismisses itself and goes back to the previous view, but it doesn't release itself from memory. I found it out by sending a notification message and that view received it. Additionally I tried to track the VM memory Allocation, and it seems the view is still in memory.
I'm using ARC and have the same method used for another view which works perfectly.
The code is fine, as posted, so here's some hints on how to proceed:
A sure fire way to be certain your view controller hasn't been deallocated is to override dealloc and log something identifiable. You can still do that in ARC, just don't explicitly call super. If you don't see the log when you expect to, then you have a problem.
Assuming that you have determined that you absolutely do have a problem, then the issue becomes finding the retain cycle. If the issue is that an instance of ResepiController isn't being dealloc'd, then you need to look for...
Any code outside the ResepiController class that has a strong reference to it. For example, if your class signs up as the delegate of some other class, make sure the delegate isn't using a strong reference.
Any internal blocks that may have implicitly retained self. Are there any blocks anywhere in your program that may have a reference to your controller at the time that you think it should be released?
I have a simple app with a tabbar navigation interface.
It has three view (A, B and C) and a modal view. Each view has its own view controller and nib . They are all designed and connected in the interface builder.
I'd like to release views which are not visible.
Tried release and nil them when another view appears such as
[[[self.navigationController.viewControllers objectAtIndex:0] view] release];
[[self.navigationController.viewControllers objectAtIndex:0] view] = nil;
etc.
It doesn't cause any issues but when I run instruments it does not make any difference. I don't see any drop in memory usage
I would appreciate your help
as #Daryl Teo wrote you should release and recreate in viewWillDis/Appear and (thats why I write this answer) you have a method called didReceiveMemoryWarning, use it!
You can simply log out whenever it gets called and test it with the Simulator included memory warning test function.
Simply open a tab, open another tab and call that test function. Your debug console should print out the log. If not you should double check if you have released all objects maybe someone is over-retained (which again should be released in viewWillDisappear).
The drop in memory usage might not be significant, depending on what the released viewController holds on to. I sugest you out a NSLog into the 'dealloc' of the viewController to see if it really gets dealloced or if there is some other object still holding on to it. Remember that release won't free the memory, it will only do so (by calling dealloc) if the objects retain count reached 0.
You don't want to do this. Let the TabBarController handle your view controllers for you. (It will already retain your viewController internally so whatever you do will only make retain count go out of sync)
You may be able to make it more memory efficient if you release objects in viewWillDisappear. Then rebuild the data again in viewWillAppear.
I've looked through lots of posts, my books and Apple Developer and gleaned most of the understanding I need on use of these. I would be really grateful if some kind person could confirm that I've got it right (or correct me) and also answer the two questions.
Many thanks,
Chris.
Order of Messages
Generally, the messages will appear in the following order:
didReceiveMemoryWarning
viewDidUnload (which can be caused by 1) - obviously only applies to View Controller Classes.
dealloc
didReceiveMemoryWarning
Called when the system is low on memory.
By default, view controllers are registered for memory warning notifications and within the template method, the call to [super didReceiveMemoryWarning] releases the view if it doesn't have a superview, which is a way of checking whether the view is visible or not. It releases the view by setting its property to nil.
Action - Release anything you do not need, likely to be undoing what you might have set up in viewDidLoad. Do not release UI elements as these should be released by viewDidUnload.
Question1 - It seems that this will be called even if the View is visible, so its difficult to see what you could safely release. It would be really helpful to understand this and some examples of what could be released.
viewDidUnload
Called whenever a non visible View Controller's View property is set to nil, either manually or most commonly through didReceiveMemoryWarning.
The viewDidUnload method is there so that you can:
- clean up anything else you would like, to save extra memory or
- if you've retained some IBOutlets, to help free up memory that wouldn't otherwise be released by the view being unloaded.
Action - generally any IBOutlets you release in dealloc, should also be released (and references set to nil) in this method. Note that if the properties are set to retain, then setting them to nil will also release them.
dealloc
Called when the view controller object is de-allocated, which it will be when the retain count drops to zero.
Action - release all objects that have been retained by the class, including but not limited to all properties with a retain or copy.
Popping View Controllers and Memory
Question 2 - Does popping a view remove it from memory?
Some corrections and suggestions:
didReceiveMemoryWarning practices
As you said, the controller's default implementation of didReceiveMemoryWarning releases its view if it is 'safe to do so'. While it's not clear from Apple's documents what 'safe to do so' means, it is generally recognized as it has no superview (thus there is no way that the view is currently visible), and its loadView method can rebuild the entire view without problems.
The best practice when you override didReceiveMemoryWarning is, not to try releasing any view objects at all. Just release your custom data, if it is no longer necessary. Regarding views, just let the superclass's implementation deal with them.
Sometimes, however, the necessity of the data may depend on the state of your view. In most cases, those custom data is set in viewDidLoad method. In these cases, 'safe to release custom data' means that you know that loadView and viewDidLoad will be invoked before the view controller uses the custom data again.
Therefore, in your didReceiveMemoryWarning, call the superclass implementation first, and if its view is unloaded, then release the custom data because you know that loadView and viewDidLoad will be invoked again for sure. For example,
- (void)didReceiveMemoryWarning {
/* This is the view controller's method */
[super didReceiveMemoryWarning];
if (![self isViewLoaded]) {
/* release your custom data which will be rebuilt in loadView or viewDidLoad */
}
}
Be careful not to use self.view == nil, because self.view assumes that the view is needed for someone and will immediately load the view again.
viewDidUnload method
viewDidUnload is called when the view controller unloaded the view due to a memory warning. For example, if you remove the view from the superview and set the view property of the controller to nil, viewDidUnload method will not be invoked. A subtle point is that even if the view of a view controller is already released and set to nil by the time the controller receives didReceiveMemoryWarning, so actually there is no view to unload for the controller, viewDidUnload will be invoked if you call the superclass's implementation of didReceiveMemoryWarning.
That's why it's not a good practice to manually set the view property of a view controller to nil. If you do, you may better send a viewDidUnload message as well. I guess your understanding of viewDidUnload is more desirable, but apparently it's not the current behavior.
Popping view controllers
If you mean 'removing from the superview' by 'popping', it does decrease the retain count of the view, but not necessarily deallocate it.
If you mean popping out from a UINavigationController, it actually decrease the retain count of the view controller itself. If the view controller is not retained by another object, it will be deallocated, desirably with its view. As I explained, viewDidUnload will not be invoked this time.
Others...
Technically, the retain count may not go down to zero. The object is more likely to be just deallocated without setting the count to zero beforehand.
Just to make sure, the view controller itself is normally not deallocated by default behaviors due to the memory warning.
didReceiveMemoryWarning
...
Action - Release anything you do not need, likely to be undoing what you might have set up in viewDidLoad.
This is wrong. Anything that you recreate in viewDidLoad should be released (and set to nil) in viewDidUnload. As you mention below, didReceiveMemoryWarning is also called when the view is visible. In didReceiveMemoryWarning, you should release stuff like caches or other view controllers you are holding on to that can be recreated lazily the next time they are required (i.e., by implementing their getter manually).
viewDidUnload
...
Action - generally any IBOutlets you release in dealloc, should also be released (and references set to nil) in this method. Note that if the properties are set to retain, then setting them to nil will also release them.
Correct. Generally, everything you create in viewDidLoad and all IBOutlets that are declared as retain should be released and set to nil here.
dealloc
...
Action - release all objects that have been retained by the class, including but not limited to all properties with a retain or copy.
Correct. It's worth noting that this includes all objects you handle in viewDidUnload because the latter is not implicitly called in the dealloc process (AFAIK, not entirely sure). That's why it is essential to set all releases objects to nil in viewDidUnload because otherwise you risk releasing something twice (first in viewDidUnload, then again in dealloc; if you set the pointer to nil, the release call in dealloc will have no effect).
Popping View Controllers and Memory
Question 2 - Does popping a view remove it from memory?
Not necessarily. That is an implementation detail that you should not be concerned about. Whatever the current practice is, Apple could change it in the next release.
Just to update this thread to make it iOS6-relevant:
viewDidUnload and viewWillUnload were deprecated in iOS6. These methods are never called.
For this and other deprecated methods, see: http://developer.apple.com/library/ios/#documentation/uikit/reference/UIViewController_Class/DeprecationAppendix/AppendixADeprecatedAPI.html
Form iOS 6 onwards, how we can check whether the view loaded again. Since "viewDidUnload" is deprecated. Are you sure "loadView" and "viewDidload" will call if the view is getting removed after "didReceiveMemoryWarning" warning.
I put an NSlog in my dealloc method of my view controller. It does not get consistently called. I do notice that ViewWillDisappear does get called always. Would it be ok to move all my tidy upcode here? Setting stuff to Nil and release calls.
Anybody got some advice on why dealloc is not getting called? I know it says in docs it may not get called, but if you have a very simple App it gets called always. So something i do must be affecting the dealloc.
This is the code that calls my ViewController than isnt always calling my dealloc.
-(IBAction) playComputerTapped:(id)sender
{
PlayGameViewController *pgvc = [[PlayGameViewController alloc]
initWithNibName:#"PlayGameViewController" bundle:[NSBundle mainBundle]];
pgvc.gameMode = 1;
[self presentModalViewController:pgvc animated:YES];
[pgvc release];
}
The above code takes me from the mailmenu ViewController into the game.
Below is the code to leave the gameViewController and take me back to the menu.
[self.parentViewController dismissModalViewControllerAnimated:YES];
Thanks
-Code
Don't you mean viewDidUnload instead of viewWillDisappear ?
viewWillDisappear is called when the view controller is disappearing. This usually happens when the view controller is being popped out, or other view controller is pushed to the stack. Purpose of viewWillDisappear is to stop active actions - for example stop animations, hide some elements or similar.
viewDidUnload is probably what you meant as this one is called when view controller's view is unloaded. This should never happen for a view controller that is currently visible, only for controllers that are somewhere in the navigation stack (part of UITabBarController or UINavigationController), but not currently visible. Purpose of viewDidUnload is to release any UI elements that are part of the view and that the view controller retained as well.
To understand it's important to realize the reasons why a view for such controller would want to be unloaded. Reason is memory consumption. Views consume considerable amount of memory, even when they are not currently visible. Views are usually very easy to reconstruct - simply by calling the code that constructed them in the first place. That's especially very easy if you are using Interface Builder. So these views are best candidates to be freed to gain more memory.
When system doesn't have enough memory it starts calling didReceiveMemoryWarning method of view controllers which are not currently visible. If you have created your controllers from template in Xcode, this method simply calls super's (UIViewControllers's) implementation [super didReceiveMemoryWarning]. That default implementation will release self.view, which in turn should deallocate it together with all its subviews.
Now let's say that your view-controller needs access to some of the subviews to manipulate it in some way. For example you can have a UILabel element there and you want to change its content accordingly. To access this element you create a member variable (IBOutlet) and connect it to the element. Now your view-controller owns that label, so its retain-count is increased. When controller's view is released, so is the label, but because your view-controller still retains the label, it will not be deallocated. Therefore you should release the label in viewDidUnload method.
I've seen applications that were creating views programmatically (in loadView method), but loading was done in such dirty way that it was not possible to reconstruct the view after it was once deallocated. Therefore each time system was out of memory, it had called didReceiveMemoryWarning which in turn deallocated the view, but after navigating back to that view-controller application had crashed. A fast "bugfix" was to remove calling [super didReceiveMemoryWarning] in view-controllers. Well, system didn't get the memory and some strange effects occurred, but at least the application didn't crash immediately.
Now the third one - dealloc. This is called when object is not owned by anyone and its memory is going to be freed. Here you need to release all objects that you have retained. For view-controllers those are usually references to model classes.
I want to describe one more possible scenario. Let's say you have a view-controller displaying a chat with another person. Let's say it's very fancy chat, with emoticons and buddy-icons being animated. Let's say that each chat-entry is displayed as a cell of UITableView.
When your buddy sends you a message, you want to append a new cell into table-view by reloading it. Therefore your view-controller has an outlet to the table-view.
In viewWillDisappear you should stop the animations of emoticons and icons.
In viewDidUnload you should release the table-view.
In dealloc you want to release chat's history (probably NSArray of all messages sent and received during this conversation).
Now if you navigate away from your chat, viewWillDisappear gets called and you stop the animations.
When system is low on memory, and your view-controller is not visible, didReceiveMemoryWarning gets called and the view is released. Your viewDidUnload gets called and you release UITableView so that it can be really deallocated.
When you navigate back to the chat, loadView gets called again and your view is constructed again, viewDidLoad is called after that. Your model (representation of chat conversation) is still there, so the table-view's datasource has all the data as before, so table-view will display exactly the same thing as before the view was deallocated. After all viewWill/DidAppear is called where you start the animations.
When you finish chatting with your friend, you release the view controller, and it gets deallocated - dealloc is called, you release an array containing the chat messages, and cleanup everything else.
I hope it make things a little clearer.
Well that depends.
If you need to reduce the memory footprint of your app, unloading stuff while the view is not visible via viewWillDisappear is a good way to go. However you'll need to re-initalize everything once the view will be shown again, depending on its content this may produce considerable overhead - maybe even without the need.
For a small app (in terms of memory usage), using dealloc to unload your stuff is fine. It will be called only if the objects retain count drops to zero and is the last method that will be run before the object is destroyed. When using autorelease, that may not be the case right away and of course the object could be retained by some object other than the parentviewcontroller, preventing it from being destroyed and thus dealloc from being called.
You might want to check out the Memory Management Programming Guide, it'll explain things more detailed.
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.