Background:
I have a tab bar application. Each tab contains navigation controller allowing the user to transition from one view to the other showing a drill down information of the data (each view is being handled by a view controller and each view controller class has didReceiveMemoryWarning method). Lists are populated by pulling the data from web services.
Problem:
When i use "Hardware > Simulate Memory Warning" option of iPhone Simulator, the didReceiveMemoryWarning method is called for ALL my view controllers - even the one which the user is viewing. I don't want to clear any content which is being used by the active view controller. How can I achieve that?
Which method should have the implementation to reload the data after the data was released because of memory warning? (I see that the view controller classes that contain a table view call viewDidLoad method when user comes back to that view, but if the view contains (say UIWebView) then viewDidLoad method is not called. Why is that?)
Edited (Friday 30 January 2009 - 03:10 PM)
(Note: I'm using Interface builder for creating views, and loadView method is commented out.)
So, when a view controller receives a memory warning message, these are the steps that are carried out:
Following method is called:
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
As a result of call to [super didReceiveMemoryWarning], [self setView:nil] gets automatically called?
If any resources should be cleared, then setView method should be overwritten to clear local resources.
[self setView:nil] is not called if the view is currently active (By default). Right? - I'm really curious which method takes this decision and how?
Can you please confirm. Plus, I was getting an error following this approach but adding myObject = nil after releasing myObject in dealloc method of controller class fixed the issue. Thanks.
This is an old question, but I don't see a proper answer, so here goes:
When a memory warning is received, -didReceiveMemoryWarning gets called in ALL view controllers, whether they are the "current" one or not. The view controllers are simply listening for the memory warning event broadcast.
If the view controller's view isn't being used at the time of the memory warning, the controller will unload it by setting the property to nil. How does it know if the the view is used? By the view's -superview property. If view.superview is nil, the view isn't part of any tree and can be unloaded safely.
Once that happens, the controller's -viewDidUnload gets called. This is the correct place to unload any outlets, and anything that will get re-created in -viewDidLoad.
So what is -didReceiveMemoryWarning for? Your controller might have objects that don't get instanced until accessed. For example, you could have a controller that sometimes needs a big chunk of data from a file, but not always. You could have a property set for it like this:
- (NSData*)bigChunkOfData {
// Get data from our instance variable _data, read from disk if necessary
if (_data == nil) {
_data = [[NSData alloc] initWithContentsOfFile:#"/path/to/data"];
}
return _data;
}
This will read the data from disk this first time, then keep it in an instance variable. Since the _data variable is created on demand, it's safe for us to unloaded it in low-memory situations: it'll just get created again next time we need it.
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
[_data release];
_data = nil; // <-- Very important: don't leave strong references dangling.
}
I do my clean up like this:
-(void)setView:(UIView*)view
{
[super setView:view];
if(view == nil)
{
// Our view has been cleared, therefore we should clean up everything
// we are not currently using
....
setView:nil is called by UIViewController in response to a memory warning, if that view is not currently visible - which is basically what you want to know.
EDITED
In answer to the follow ups:
Correct.
That's what I do, and it works for me.
Correct. The implementation of didReceiveMemoryWarning in UIViewController is what does this. If you don't override didReceiveMemoryWarning, then the base class implementation in UIViewController will be called - if you do override it, obviously you should call:
[super didReceiveMemoryWarning]
To ensure that I dont have to handle this for every single viewcontroller I write.. I have just made a Xcode ViewController template which provides guidelines on which objects to release and when..
more explanation here http://iphone2020.wordpress.com/2010/05/30/efficient-memory-handling-in-uiviewcontroller-part-1/
Hope it finds useful.
In regard to the view management and memory warnings:
UIKit doesn’t only allow navigation back from a view controller, but also allows navigation to other view controllers from existing ones.
In such a case, a new UIViewController will be allocated, and then loaded into view.
The old view controller will go off-screen and becomes inactive, but still owns many objects – some in custom properties and variables and others in the view property/hierarchy.
And so does the new visible view controller, in regard to its view objects.
Due to the limited amount of memory of mobile devices, owning the two sets of objects – one in the off-screen view controller and another in the on-screen view controller – might be too much to handle.
If UIKit deems it necessary, it can reclaim some of the off-screen view controller’s memory, which is not shown anyway; UIKit knows which view controller is on-screen and which is off-screen, as after all, it is the one managing them (when you call presentModalViewController:animated: or dismissModalViewControllerAnimated:).
So, every time it feels pressured, UIKit generates a memory warning, which unloads and releases your off-screen view from the view hierarchy, then call your custom viewDidUnload method for you to do the same for your properties and variables.
UIKit releases self.view automatically, allowing us then to manually release our variables and properties in our viewDidUnload code.
It does so for all off-screen view controllers.
When the system is running out of memory, it fires a didReceiveMemoryWarning.
Off-screen views will be reclaimed and released upon memory warning, but your on-screen view will not get released – it is visible and needed.
In case your class owns a lot of memory, such as caches, images, or the like, didReceiveMemoryWarning is where you should purge them, even if they are on-screen; otherwise, your app might be terminated for glutting system resources.
You need to override this method to make sure you clean up your memory; just remember you call [super didReceiveMemoryWarning];.
An even more elaborate explanation is available here: http://myok12.wordpress.com/2010/11/30/custom-uiviewcontrollers-their-views-and-their-memory-management/
Fortunately, the simulator has a handy function that allows you to put low-memory situations to the test. Put some NSLog() statements in both viewDidLoad and didReceiveMemoryWarning, like this:

- (void)viewDidLoad {
NSLog(#"viewDidLoad");
...
}
- (void)didReceiveMemoryWarning {
NSLog(#"didReceiveMemoryWarning");
}
Related
When we get low memory warning, we release all the views, set outlets to nil, and recreate them all over again.
but for views added in the interface builder, they are only added when we initWithNibName is called.
So, is it in general bad practice to use a lot of views via interface builder or that its automatically taken care of by original UIViewController implementation of didReceiveMemoryWarning(iOS 6+) or viewDidUnload (iOS 5 and below) ?
The default behavior for a viewcontroller is to load its view hierarchy when the view property is first accessed and thereafter keep it in memory until the view controller is disposed of. There is no indication that a view created using code is different from one created in Interface Builder. I personally prefer InterfaceBuilder owing to app maintenance since UI code gets complex pretty fast and separating UI elements to the functional aspect of UI elements is a clean bifurcation making it easy for a new programmer to maintain your code base.
But when it comes to releasing memory didReceiveMemoryWarning delegate is to be used to explicitly release the view hierarchy if that additional memory is necessary for your app.
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Add code to clean up any of your own resources that are no longer necessary.
if ([self.view window] == nil)
{
// Add code to preserve data stored in the views that might be
// needed later.
// Add code to clean up other strong references to the view in
// the view hierarchy.
self.view = nil;
}
return;
}
note here that we test if the view controller’s view is not onscreen. If the view is associated with a window, then it cleans up any of the view controller’s strong references to the view and its subviews. If the views stored data that needs to be recreated, the implementation of this method should save that data before releasing any of the references to those views. By storing either you can keep it in memory as part of previous viewcontroller's memory or store it in coredate or some other persistent (disk cache etc) way.
The next time the view property is accessed, the view is reloaded exactly as it was the first time. Pictographically it makes perfect sense -
[source]
I'm pretty new to programming and I started learning ObjC and the CocoaTouch Framework.
I've learnt about the delegation pattern and I'm pretty comfortable using it, but maybe I'm using it too much.
There is one thing I do not understand and it's specific to UIKit view controllers, I've read a lot of posts about it but I'm not able to find a clear answer.
Memory management with delegates?
Why are Objective-C delegates usually given the property assign instead of retain?
Let's suppose that I have a navigation controller and I'm pushing view controllers through it, imagine that the visible controller has as a delegate (assign not retain) a non-visible controller in the stack. A memory warning is incoming and all the view controllers (except the visible one ) are unloaded using the viewDidUnload and dealloc method, the delegate will be unloaded and the "callback" never sent.
If the new view controller is not pushed but presented modally the "connection" between delegate and the view controller is never lost, viewDidUnload is never called in the parent view.
Here is my question:
Is it correct to use delegation pattern between two view controllers?
It is certainly a correct design, depending on your controllers semantics. I used this when having a "master" view controller managing (and receiving delegate calls) from "slave" view controllers.
As to your analysis of what happens in case a memory warning is sent, there is possibly a slight misunderstanding, in that the view controller actually receives the viewDidUnload, but what this means is that the view controlled by it has been unloaded to get back some memory, so the controller can do its part of cleaning (as usual). The view controller is not itself "unloaded" or released or whatever. So the callback will always be sent. The only thing is that if the view had been previously unloaded, you would need to restore it.
Keep also in mind that, if is utterly impossible for you to recreate your unloaded views, you can prevent a specific view from being released by not calling super in your didReceiveMemoryWarning override. Take this suggestion "cum granum salis", however!
In the end, no need to use a Modal View.
Indeed, this is what Apple Docs say:
(void)didReceiveMemoryWarning :
The default implementation of this method checks to see if the view controller can safely release its view. This is possible if the view itself does not have a superview and can be reloaded either from a nib file or using a custom loadView method. If the view can be released, this method releases it and calls the viewDidUnload method.
(void)viewDidUnload:
This method is called as a counterpart to the viewDidLoad method. 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. Because view controllers often store references to views and other view-related objects, you should use this method to relinquish ownership in those objects so that the memory for them can be reclaimed. You should do this only for objects that you can easily recreate later, either in your viewDidLoad method or from other parts of your application. You should not use this method to release user data or any other information that cannot be easily recreated.
Your UIViewController is fairly lightweight and should never be unloaded in a low memory condition. The UIView owned by the view controller is very heavyweight and will definitely be unloaded in low memory conditions. Your controller should be ready to recreate the view if necessary, but you'll never need to regenerate your stack of view controllers.
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.
I am using UIImagePickerController to take a photo from the camera. However, the I find that randomly my calling controller (the one that is shown before the UIImagePickercontroller is shown) gets unloaded. I logged the viewDidUnload, and indeed it does get called. When the camera is done and dismissed, my controller's viewDidLoad will be called, unfortunately all the state is now gone. Things like text entered or things selected will be gone.
Obviously this is something to do with running out of memory. But is this behavior normal? Should I be handling it? This is NOT typically how modalViewController works. Usually you show it and dismiss it, everything should be intact.
What is a good way to avoid data lost in this case? Should I have to write a bunch of code to save the full state?
what's happening is that your view controller's didReceiveMemoryWarning is being called, and then since your view isn't visible, it's being unloaded.
the solution is that any data that wants to be persistent should be stored in your view controller rather than in your view.
you can prevent this from happening by providing an implementation of didReceiveMemoryWarning in your UIViewController class that doesn't call [super didReceiveMemoryWarning] which is where the unloading of the view happens, but it's still a good thing to try to understand what's going on.
It's really not advisable to override -didReceiveMemoryWarning so that UIViewController can't deallocate the view because then you may run into a "real" problem of low memory and your app will be forced to quit by the system.
What you really should do is store the text and other bits of entered data from the view in your -didReceiveMemoryWarning method (making sure to call super). Then in -viewDidLoad you can put the text and other bits back into the UI.
This will lighten the memory usage and reduce the likelihood of your app being forced to quit because there's no more memory left. All the while, the user won't know it ever disappeared!
My solution to the same issue described above was to declare an instance variable in my view controller which stores the contents of the UITextView should the view get unloaded. Thus in my viewWillDissapear method I save the contents of UITextView.text into my instance variable and in my viewWillAppear method I restore the contents.
- (void)viewWillAppear:(BOOL)animated
{
if (messageTextString != nil)
{
textEdit.text = messageTextString;
}
[super viewWillAppear:animated];
}
(void)viewWillDisappear:(BOOL)animated
{
self.messageTextString = textEdit.text;
[super viewWillDisappear:animated];
}
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.