What should I do when my app recieves a memory warning?
It all depends on your app, usually you don't have to do anything special except following Apple's recommended practices.
ViewControllers which are not visible at the moment will get didReceiveMemoryWarning message. By default (calling [super didReceiveMemoryWarning]) controller's view is unloaded (released, freed). As the view is unloading, view controller receives viewDidUnload where you should release all your IBOutlets (or otherwise retained UI elements). Only then the view can completely be deallocated and memory freed.
In the didReceiveMemoryWarning you should also free as much data as you can - if you store some part of data model in ViewController, release it, and reconstruct in viewDidLoad that would be called when your view is loaded again (when user navigates back to this controller). You can inform your model classes to free memory too.
It really depends on your app.
If your app downloads and caches lots of contents from Internet for example, you should purge as much as possible when receiving a warning.
If your app is an OpenGL game, you might have a texture/sound/data manager which references some unused data, which you then want to free. Cocos2D manages this kind of things.
If your app isn't memory intensive, you have a memory leak somewhere, and you should 1) read the Memory Management Programming Guide by Apple 2) use Instruments/Leaks.
In didReceiveMemoryWarning, you should release any cached or non-essential items to prevent running out of memory completely.
If you log or write to any other file, there might be an issue with "disk" space.
Also you should check for memory leaks.
Related
I've never personally seen didReceiveMemoryWarning called before. Is there a known threshold of memory usage where it gets called?
It's called when the system has low memory, then you need to purge your UIViewController.
You can simulate a memory warning in the Simulator: Hardware > Simulate Memory Warning and see what happens.
If you are supporting prior versions to iOS6 it's important to take note of the following from Apple's documentation:
Prior to iOS 6, when a low-memory warning occurred, the
UIViewController class purged its views if it knew it could reload or
recreate them again later. If this happens, it also calls the
viewWillUnload and viewDidUnload methods to give your code a chance to
relinquish ownership of any objects that are associated with your view
hierarchy, including objects loaded from the nib file, objects created
in your viewDidLoad method, and objects created lazily at runtime and
added to the view hierarchy. On iOS 6, views are never purged and
these methods are never called. If your view controller needs to
perform specific tasks when memory is low, it should override the
didReceiveMemoryWarning method.
About the threshold, I try not to exceed 12MB of memory.
No. It is called when springboard feels that there are too many resources using memory on the device. Depending on how much stuff other apps are using on your phone, it can happen sooner or later. You can never reliably calculate or rely on when it will happen.
Also be warned, because the memory reported in tools like allocations is not necessarily correlated to the footprint of your app, so it can look like you are not using much, but you may still get this warning.
from the documentation , when the system determines that the amount of available memory is low then this method is called. system determines low memory itself , when your app allocations exceed the amount of memory available to your app then this method is called. Please refer http://developer.apple.com/library/ios/#documentation/uikit/reference/UIViewController_Class/Reference/Reference.html
From Apple's View Controller Programming Guide / Managing Memory Efficiently;
didReceiveMemoryWarning
Use this method to deallocate all noncritical custom data structures associated with your view controller. Although you would not use this method to release references to view objects, you might use it to release any view-related data structures that you did not already release in your viewDidUnload method. (The view objects themselves should always be released in the viewDidUnload method.)
viewDidUnload
You can use the viewDidUnload method to deallocate any data that is view-specific and that can be recreated easily enough if the view is loaded into memory again. If recreating the data might be too time-consuming, though, you do not have to release the corresponding data objects here. Instead, you should consider releasing those objects in your didReceiveMemoryWarning method.
http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/BasicViewControllers/BasicViewControllers.html
For didReceiveMemoryWarning, we are recommended to deallocate non-critical data structures. So, what is critical and what is non-critical?
Also, it says to release what we did not already release in viewDidUnload.
But when there is a memory warning didReceiveMemoryWarning is called and view could be unloaded then viewDidUnload is called. So, is it talking about the moving those codes to the former event's method(didReceiveMemoryWarning) or am i missing something about the order of events?
For viewDidUnload, we are recommended to care about easily recreating the data when reloading a view.
So, if a view is in use and could not be unloaded, why will we release time-consuming data in didReceiveMemoryWarning? After those data released, when user tries to do something within the current view, then it will be time-consuming to load them too.
First of all, these are just guidelines, so if you don't think it makes good sense to release something in didReceiveMemoryWarning then don't do it. But keep in mind that if your application is the one that is causing the memory warning in the first place then it will eventually just be terminated by the OS.
Re (1): Critical vs. non-critical is entirely your call. Only you can really determine what data you're holding that you consider critical. Though it's probably going to be closely related to your (3), that is, something that is easily recreated is probably not too critical.
Re (2): I don't think the statement is in reference to the order of the calls. As you have realized, in general, viewDidUnload is going to get called after didReceiveMemoryWarning (since didReceiveMemoryWarning can cause viewDidUnload to be called). As an example, in viewDidUnload one will release references held to UI elements from a nib. So don't also release them in didReceiveMemoryWarning.
Re (3): If a view is in use and thus can't be unloaded, then yes, obviously it doesn't necessarily make much sense to release it in didReceiveMemoryWarning. However, you may actually have instances where a view couldn't be unloaded, but is known to not be visible (not terribly normal), in which case it might make sense to unload it's data and recreate it when the view is visible once again.
Additionally, I agree the "Instead, you should consider..." remarks are a bit odd, but again I think the point of recommending the data be released in didReceiveMemoryWarning stems from the fact that if you're receiving those warnings then your own app might be in danger of being terminated. So while it is currently the case that viewDidUnload probably is always called as the result of a memory warning, that may not always be the case in the future and so conceptually it makes more sense to release the data in didReceiveMemoryWarning itself. In didReceiveMemoryWarning you KNOW there is memory pressure, in viewDidUnload you may not. So while it's true that the data will then be expensive to recreate, that might be better than having your app terminated. To the user it will appear as though the app crashed.
My own personal approach to those methods is generally this:
viewDidUnload - Release any references to UI elements loaded from a nib. Release any UI elements I created myself in viewDidLoad.
didReceiveMemoryWarning - Release any data that is used in the presentation of the UI that I can recreate if/when viewDidLoad is called again (or some other app specific event). Do NOT release anything that I can't possible recreate.
The apps I'm building are graphics-intensive, both in terms of layout chrome, and in the images associated with the data I'm downloading and displaying.
My didReceiveMemoryWarning methods are ALL about determining which images I'm holding in memory but not currently displaying, and dropping them from memory. The other piece of that is, you need to check before displaying an image whether it's still around, and lazy-load it if not.
Aren't these methods called when the app is about to be shut down? If so, then won't the memory be all cleared out anyway?
If you only have one view that lasts the duration of the app, then unload and dealloc are currently never even called, so these methods are actually unused and unneeded.
However, if you ever expand this app to have views and objects that get switched in and out of use, then in low memory circumstances these methods may well be called to lower your app's memory footprint so that the app doesn't get killed for using too much memory. So leaving them in (and coding them correctly to release internally allocated objects and malloc'd memory) for future code reuse is considered good practice. That's why they come with the various Cocoa templates.
Aren't these methods called when the app is about to be shut down? If so, then won't the memory be all cleared out anyway?
It is true that viewDidUnload and dealloc are called when an app terminates, but these are certainly not the only times. It is very important to correctly implement these cleanup methods, as well as didReceiveMemoryWarning.
If you don't clean up properly in dealloc, then your app will start to leak memory. Over time, it may consume more and more memory, until it gets terminated by the system.
Similarly, if your viewDidUnload doesn't release its resources, you can be leaking memory. If the view is used multiple times, each invocation will leak.
Careful memory management is more important than ever with iOS 4, as your application may end up in the background if the user presses the Home button. This means it may run for longer than ever, and thus you will be reusing the same view controllers when it regains the foreground. If your app doesn't release unused memory properly, it will almost certainly be killed by the system.
iOS Memory Management Programming Guide
viewDidUnload is only called in low memory situations. You want to release all object you create in viewDidLoad. You want to pair them up. You still want to release everything in dealloc, since viewDidUnload will not be called if low memory situations never occur in your app.
Keep in mind that each class inerithing from NSObject has its dealloc and so when the reference count of an object reaches 0 , its dealloc is being called, meaning that the memory owned by that object would better be deallocated.
Similarly viewDidUnload is a method that each UIViewController has and it is being called when the main view associated to the controller is no more needed, no more visible if you want (you can think of it being called when you a pop the controller from a navigation stack or switch a tab in tabbar controller). It is convenient for the app and the iPhone/iPod not to have the objects owned by the view around when the view is not displayed/active/used etc.
Finally the AppDelegate, as an object has its own dealloc method, so maybe your confusion can come from this point.
I am introducing auto-rotation to my app and I'm having an issue with a memory warning. Whatever orientation I start my app in, as long as the device remains in that orientation, I get no memory warnings. However, the first time I rotate the device the following warning is placed on the console: Safari got memory level warning, killing all documents except active. When this happens all view controllers, other than the one be viewed, are unloaded - this produces unexpected behaviors when navigating back to view controllers that should normally already be on the stack. The app never crashes and this warning occurs once upon the first rotation, after that it never happens (until I stop/start the app again). Also, this only happens on the device - no memory warning when running in simulator.
Has anyone seen this behavior? In any case, does anyone have any suggestions on what I might try in order to remove the memory warning.
Thanks in advance.
You can't assume that memory warnings will never happen; you have to handle them gracefully. Suggestions:
Check for memory leaks with Leaks (note that it doesn't catch all leaks).
Fix your view controllers to handle a view reload. Specifically (unless you override -(void)loadView), it'll call -(void)viewDidUnload on a memory warning and -(void)viewDidLoad when it becomes visible again. You might fix this by saving state in the view controller and restoring it to the views in -(void)viewDidLoad.
If you can't be bothered handling the memory warning, implement -(void)didReceiveMemoryWarning and do not super-call (i.e. comment out [super didReceiveMemoryWarning]). This is lazy, and might cause a crash if your app ends up using too much memory (background apps like Safari and Phone will be killed first).
You can test memory warning behaviour with the "simulate memory warning" option in the simulator.
Memory warnings are part of a normal iOS behavior, due to its limited memory, especially now that multi-tasking is supported.
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/
In a multi-view application, do you think its better to let views automatically get unloaded in memory tight situations, or to actually only ever have one View Controller allocated at a time, and when you switch views, the new one is created, the old one removed, the new one adde d and the old one released. Deallocating every time also means that there is a slight delay when switching to a new tab (very slight). So What do you think?
Also, I'm a bit confused about how and when and where and by who views are released (through viewDidUnload) automatically. If someone could clarify that for me, thanks.
In general, don't unload views unless you have to (didReceiveMemoryWarning) or it makes sense (something like a login form that's unlikely going to be used again).
You can't really assume that you have a fixed amount of memory. iPhone's have less memory than iPod touches. iPhone 3GS's have more memory than either. Jail-broken handsets often have substantially less memory.
By only releasing views when you have to you're making your app run faster on the 3GS and allowing it to run when there's less memory available.
The didReceiveMemoryWarning method releases the view if it is not visible. The following is from the documentation (v3.x):
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.
Obviously you also need to release any cached data. SDK2.x does not have the viewDidUnload method.
Depends, if you have a bunch of Views that the user switch back on forth from often then I would say to keep those in memory at that time, if theres views where the user wont come back to for awhile then you can probably unload that viewController and save memory. However if you have views that are taking up a ton of memory each then it might be wise to unload the viewcontroller when its not in use. Its really a matter of how often you are going back to the views and how much memory the view takes, also how many views you have. Taking these things into consideration you should be able to make a good decision of when to keep the viewControllers around and when the unload them. I believe that the view is a round as long as the ViewController is around (unless u release it explictly, which might have bad side effects (dont know)) , viewdidUnload just tells you that the view was unloaded of the screen, not too sure on that point tho.