didReceiveMemoryWarning and viewDidUnload - iphone

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.

Related

Deallocation when iPhone is turned back on from sleep and app is still open

There are a few instances where my app crashes when turned back on from sleep and the app is still open. It happens if a UITableViews is open when the iPhone is put into sleep; the table is being filled from an XML being parsed. I understand why it is crashing; in my viewDidUnload method I am releasing the array that is filling the table. How to handle this I am not sure; yes I could simply not release it in viewDidUnload, but then it would never leave memory if you returned to the main menu.
Any help would be appreciated!
The method viewDidUnload is not the right place to release your data if at all. I quote Apple's documentation, which desribes it better than I could:
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.
ViewDidUnload is used only to release view related objects. A view controller can release its view because its not shown, still your instance of that controller exists and so does your model.

What to do when my app receives memory warning?

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.

Why bother with releasing memory in viewDidUnload and dealloc

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.

iPhone dev - In didReceiveMemoryWarning or every time

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.

What sort of cleanup work should I do aside from dealloc in an iPhone app?

Right now, I do most of my cleanup work in dealloc (cleaning up IBOutlets, allocated objects, etc.). What other places should I do cleanup work in order for my app to be a well-behaved one? Could you explain the things that are typically done in those methods as well?
For example, viewDidUnload, applicationWillResignActive, etc.
For views, I typically release any UI widgets that were created from the NIB file in viewDidUnload. Any models or other objects I clean up in the viewController's dealloc.
Sometimes I have views that create a model (say a Dictionary of section names to section rows) from a primary data object. If I create/build an object in viewDidLoad I will release it in viewDidUnload (since my viewDidLoad will get called again when the time is right).
I believe that in SDK 3+ you don't have to typically worry about implementing didReceiveMemoryWarning directly as the new viewDidUnload method is the main place to do your view cleanup.
For normal objects (objects without special life cycles like a view controller has) I just release their member vars in the dealloc.
Don't forget:
- (void)didReceiveMemoryWarning
Note: This "Answer" is only relevant to app quit/termination.
According to the answer I received to my question, it's not even necessary at all to do cleanup work like cleaning up IBOutlets, allocated objects, etc. Just save state (as necessary) when your app quits, and let the iPhone OS handle the final cleanup.
Note that your question is ill-formed. The -dealloc method of UIApplication is never called. The -dealloc of your application's delegate is never called. That means that any objects that are retained by your application's delegate will never be released, so their dealloc is never called.
You should be doing your cleanup in your application delegate's applicationWillTerminate:
Since your application is about to die, you don't really need to do anything except give back non-memory resources, make sure your data files are properly closed, and that your NSUserDefaults are synchronized so you can restart properly the next time you are run.
However, any object that might be allocated and deallocated repeatedly over the life of the program deserves a proper Obj-C dealloc method, as documented by Apple, and it is good practice to write this for all your classes, even though they won't be called, just so you build good habits, and readers won't be confused. Also, it saves maintenance headaches in the future, when you DO create and destroy multiple of these, for example in your unit tests.
I would use the [yourObject release] method, but replace yourObject with an object