iPhone dev - In didReceiveMemoryWarning or every time - iphone

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.

Related

didReceiveMemoryWarning and viewDidUnload

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.

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 rotation causes memory warning

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/

How to deal with low memory warnings on the iPhone?

I'm having problems with my application receiving low memory warnings while the user is deep within a navigation controller stack of views. After the user browses through a bunch of hierarchical options in subsequent UITableViews, he can open a PDF document in a UIWebView (in a different view controller).
Everything works fine, the PDF loads and the user can flip through the pages. However, when the document is a bit large, or has several pages, and the user taps on the "Back" button in the navigation controller, he app crashes as the previous view controller in the navigation controller stack has been deallocated.
After searching around for ways to deal with low memory warnings, and dealing with this type of problem, I found several posts that just advise people to release the objects that can be released, and then lazy-load them later on when the user tries to load a view that has been deallocated. One of these posts is Craig Hockenberry's Dealing with memory loss: the cleanup post.
While that's a bit helpful, it doesn't give me much information to work from.
Can someone provide a simple guide on how to deal with low memory warnings, and how to implement "lazy loading" of objects?
When memory is low, the system sends out a series of memory-related messages. Any instantiated view controllers will get the -didReceiveMemoryWarning message. If you don't implement this, the default action (assuming the view controller is not front-most) is to release the controller's view member. If you've got hooks into it, or perhaps into its subviews, that could cause issues when your controller gets returned to the top of the stack.
Your first task is to figure out what exactly is causing the problem. Which object is being deallocated? Usually, fixing this is simply a matter of making sure the object is retained properly.
I suggest you use NSZombiesEnabled to try and track down the object you're having trouble with. Once you have that, you can make sure you're retaining it properly.