When the app receives a low memory warning message, 3 situations can happen :
your app has just been launched and the user has not done anything special
the app is running and there is a current context
the app is in the background with some running context
So when you receive this message, your are supposed to free memory... But where ? And how ?
I understand that :
initWith..... must set the default static values.
viewDidLoad must load any non static object
didReceiveMemoryWarning must free those non static objects
I don't see what can/must be done in viewDidUnload...
I guess some retained values must be set to nil somewhere... in didReceiveMemoryWarning ?
And what must be done with the active context (positions of thing of the screen, displayed text, ...) so when viewDidLoad is called again, those thing appear again as they where before the memoryWarning call ?
I mean, imagine 2 scenarios :
Scenario 1
you are working on something... you wrote some text in a field, did not saved it, opened another view, moved a view on the screen.
You send the app in background.
Then a memoryWarning is sent to the app.
After that, the user send the app to foreground : it should display things like they was on exit, on the current view as on previous views, but if everything has been released, how may you do this ?
Scenario 2
you are working on something... you wrote some text in a field, did not saved it, opened another view, moved a view on the screen.
Then a memoryWarning is sent to the app.
You don't want to loose what is on the view, nor what was on the previous view. You don't want neither the screen to flicker because of a release / reload feature. How do you deal with this ?
So when those memory warning happens, do you have any other choice than writing things to disk to display them back later ?
And when do you load those again ? I have a viewController that loads (viewDidLoad), receive a memoryWarning, unloads (viewDidUnload), but when going back to it, viewDidLoad is not called again ? Do this must be done in viewWillAppear ? Do I have to think that anytime viewWillAppear is triggered, I can assume that things that are supposed to be displayed on it are loaded ?
Any help, even with valuable links, would be great !
Thank you for your help.
My idea is that two methods are called when an app receives a low memory warning:
didReceiveMemoryWarning // in your NSObjects
and
applicationDidReceiveMemoryWarning // in your app delegate
then, if you want to free memory, these are the methods to consider.
As regards what you can do in there... Well...
Think about what Xcode suggests:
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
/*
Free up as much memory as possible by purging cached data objects that can be recreated (or reloaded from disk) later.
*/
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
Seemingly, the best choise will be freeing any object not seen by the user and/or not in use, or any cached data that you can recreate later.
Do not touch your GUI: if you close it or part of it your app becomes unusable and the user disappointed.
Regarding your 2 scenarios, I see a possible mistake in considering what memory warning are for.
They are a way to treat an emergency and not the normal way to manage memory.
Developpers must think of a good memory architecture and save data whenever possible.
In scenario 1, save your data when the app is sent to background.
applicationDidEnterBackground
In scenario 2, save your data when a new view is opened.
Hope this makes sense...
Consider the alternatives to your scenarios. Your app could be killed if it does not free enough memory, which would be even more jarring to the user. One might choose potentially flickering the current display over losing the user's valuable data.
Related
If I stop using my app for a long time then go back to it, it seems like the viewDidLoad method is being called again, even though I never "exited" the app (by pressing the home key twice and tapping the X) nor did I reboot the iPhone. If I just go to the home screen and then open the app again after a short time this does not happen. What is going on?
This has to do with the way that the operating system manages memory and how it deals with having many different apps open at one time. In a short summary, if your app is in the background for a long period of time, eventually, the OS will decide that it is inactive, and the memory associated with its view will be marked for reuse somewhere else where it is more needed. This is the viewDidUnload method in your view controller. Any time the viewDidUnload method is called, the viewDidLoad method will be called again so that you get a chance to reload your view before the user sees it.
Edit:
You cannot rely on this phenomenon happening every x minutes. It will only happen as the OS requires more memory for active apps. If you want to make sure the user always gets updated information when he or she resumes usage of your app, use NSNotificationCenter and register for the UIApplicationDidBecomeActive notification.
You can try to monitor the memory waring. Most of situation like this is memory warning get trigger and viewDidUnload get called after that if the view is not the top most. So when you back to that view, viewDidLoad will get called again.
You can try to override this method in UIViewController and see if memory warning get called. It maybe triggered by some other component in your application.
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
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.
I have an app (a game) which saves data: game state, high scores, achievements, etc. Currently the app delegate does this on applicationWillTerminate:. After playing around with iPhone 4 for a bit, it seems that applications pretty much never terminate: they just run in the background forever, unless the user goes out of their way to quit them, or restart the phone.
So my question is, should I find another place to save my data, and if so, when?
To minimize the amount of time spent in the delegate method call, you should find a place that makes sense to save during the game (level completion, checkpoints, etc). You can also add a new delegate method to your application delegate which will be called when your application transitions to the background where you can duplicate some of the things you may have done previously in applicationWillTerminate:. The new delegate method to implement is -applicationDidEnterBackground:.
You will also receive a notification that the user switched back to your app as applicationWillEnterForeground:.
you can do so in the views diddisappear delegate method
-(void)viewDidDisappear:(BOOL)animated
{
//CODE FOR SAVING
}
There are 2 App delegate methods you can use
applicationDidResignActive: //pausing the app, used when a msg comes up. if multitasking this wont help
applicationDidEnterBackground: // called in iOS 4 for when the app is in the background
you can see when it loads into the foreground using
applicationWillEnterForeground:
check out the reference for more info
You should save in applicationDidEnterBackground. Make sure to wrap your saving code with – beginBackgroundTaskWithExpirationHandler: and endBackgroundTask, since without that, you have less than a second (or something like that) before execution suspends.
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.