How to implement didReceiveMemoryWarning? - iphone

I have developed a simple location aware iPhone application which is functionally working very well to our expectations except in the low memory condition of the phone .
In low memory condition of the phone my app just crashes and If I increases the phone memory by freeing up some space it again start working well without any crash .
when I did some googling on the problem I found that in the low memory conditions the OS will send didReceiveMemoryWarning to all the controllers in the current hierarchy so that each one of them should implement didReceiveMemoryWarning method and also set iboutlet to nil for the view that is currently not visible .
I have also read somewhere that if the view for that controller is not visible the method setView with nil parameter will be called and if there are some outlet variables attached to view there will be problem in removing them.
So with all these fundas what is the best to handle low level memory condition raised by the Iphone by implementing the didReceiveMemoryWarning and viewDidUnload methods.
Please give a proper example or link if possible for the solution of the above problem .
thanks.

One example i am posting...which i have copied from somwhere... it might give you some idea...
- (void)didReceiveMemoryWarning {
// Release anything that's not essential, such as cached data (meaning
// instance variables, and what else...?)
// Obviously can't access local variables such as defined in method
// loadView, so can't release them here We can set some instance variables
// as nil, rather than call the release method on them, if we have defined
// setters that retain nil and release their old values (such as through use
// of #synthesize). This can be a better approach than using the release
// method, because this prevents a variable from pointing to random remnant
// data. Note in contrast, that setting a variable directly (using "=" and
// not using the setter), would result in a memory leak.
self.myStringB = nil;
self.myStringD = nil;
[myStringA release];// No setter defined - must release it this way
[myStringC release];// No setter defined - must release it this way
/* 3. MUST CONFIRM: NOT necessary to release outlets here - See override of
setView instead.
self.labelA = nil;
self.imageViewA = nil;
self.subViewA = nil;
*/
// Releases the view if it doesn't have a superview
[super didReceiveMemoryWarning];
}

Memory warnings are a signal to you that you should dispose of any resources which aren't absolutely critical. Most of your controllers will be hanging onto data caches, intermediary data, or other bits and pieces, often to save recalculation. When they receive memory warnings, they should begin flushing anything they don't immediately need in order to operate.
How you determine what is "critical" depends entirely on your application's design. An OpenGL game, for example, may determine that textures currently on-screen are valuable and flush textures which aren't visible, or level data which is outside the bounds of the current play area. An application with extensive session logs (like an IRC client) may flush them out of memory and onto disk.
As you observed, the warning is sent to each controller in your hierarchy, so each piece needs to determine individually what data constitutes "critical for operation" and what constitutes "expendable". If you've optimized them all and are still getting out of memory warnings, it's unfortunately time to revisit your core application design, because you're exceeding the limits of the hardware.

On iOS 5 and earlier.
When controller receive a memory warning, didReceiveMemoryWarning will be called. At that time, if controller's view is not in the view hierarchy, the view will be set to nil and viewDidUnload will automaticly invoked. So the things we must do in viewDidUnload is releasing sub view created in viewDidLoad or created from Nib. Like this:
- (void)viewDidUnload
{
self.subView = nil;
self.subViewFromNib = nil;
}
- (void)didReceiveMemoryWarning
{
self.someDataCanBeRecreatedEasily = nil;
[super didReceiveMemoryWarning];
}
On iOS6.
The controller doesn't automaticly release the view when receive a memory warning. So the viewDidUnload never be called.
But we still need to release our view (including sub view) when a memry warning happens.
Like this.
- (void)didReceiveMemoryWarning
{
if ([self isViewLoaded] && [self.view window] == nil) {
self.view = nil;
self.subView = nil;
self.subViewFromNib = nil;
}
self.someDataCanBeRecreatedEasily = nil;
[super didReceiveMemoryWarning];
}
Note that we don't call [self view] before we know the view is loaded. cause the this method will automaticly load view if the view is not loaded.
Note that we can release the view just when the view is not added to a window.

It's up to you to decide what to do in didReceiveMemoryWarning. The OS is telling you that memory is low, and you need to free up as much as you can as soon as you can. The idea is that you should free any cached data, unload views that aren't visible, etc. The details are application specific.

You could also release memory in didReceiveMemoryWarning, that you allocated for static variables in your classes. Because once the memory for static variables is allocated it'll not be freed during the application runs.

To my surprise, only a few apps in the official iPhone samples implement didReciveMemoryWarning. You can use the iPhoneCoreDataRecipes example as an reference.
Some samples (e.g. TableViewSuite) even do something else ;-)

Related

Random crashes occur in my iphone project, see two call stacks, all in web core, all crash after UIWebView deallocate

Random crashes occur in my iphone project, see two call stacks, all in web core, all crash after UIWebView deallocate.
See crash 1, seems web core can't close it completely, is it blocked by other running threads?
See crash 2, why it is still dealing with some issues even if UIWebView has been released?
Any idea or discussion will be appreciated, thanks in advance.
Crash 2 has higher reproducible rate than crash 1, scan WebCore source code, it seems be manifest cache has been deleted (who did it?) when try to access it.
My guess is that you're setting some object (probably your view controller) as the delegate of your webView, either via Interface Builder, or via code that looks something like this:
- (void)viewDidLoad
{
[super viewDidLoad];
self.webView.delegate = self;
}
- (void)dealloc
{
[self.webView release];
[super dealloc];
}
This code has a serious bug that's not immediately obvious if you don't know what to look for. Can you spot it?
When the controller gets dealloc'd it releases the webView, but this doesn't necessarily mean the web view no longer exists. It may still be retained by some code down inside its internal implementation, and it may still be loading content.
When it finishes loading, the web view will call a method like webViewDidFinishLoad: on its delegate. The problem is, your view controller was the delegate, and your view controller no longer exists. The section of memory where your view controller used to live has been reclaimed by the operating system, and is no longer accessible to your application process. The web view doesn't know this though. It still has a reference to that memory address in its delegate property. So it tries to call a delegate method on that object, and whoops... EXC_BAD_ACCESS.
Crashes like this appear to be random since they depend on what your app is doing in the background at the time. This makes them tricky to diagnose. Fortunately they're easy to solve. Simply set the object's delegate to nil before you release it your dealloc statement, like this:
- (void)dealloc
{
self.webView.delegate = nil;
[self.webView release];
[super dealloc];
}
By setting the web view's delegate to nil before you release it, you guarantee that the web view won't try to call delegate methods on your object after it's been deallocated.
If you're using ARC, your class may not have a dealloc method. That's normally fine, but in a case like this you should go ahead and add one. You don't want to call release if you're using ARC, but you should still add a dealloc method and set the delegate property to nil.
To avoid similar crashes in the future, apply this best practice any time you set your object as the delegate of another object.
Before releasing the webview, try to reset the delegate an stop loading.
[webView stopLoading];
webView.delegate = nil;
[webView release];
webView = nil;

releasing in viewDidUnload makes any difference or not?

I am new to iPhone development.
I am using some buttons and labels in my app. I am releasing all these buttons and labels in dealloc function.
I am also releasing them in viewDidUnload.
- (void)viewDidUnload
{
[super viewDidUnload];
self.ans1 = nil;
self.ans2 = nil;
self.ans3 = nil;
self.ans4 = nil;
self.button = nil;
self.button2 = nil;
self.button3 = nil;
self.button4 = nil;
}
I just want to know that it is good for memory management or it makes no difference to memory management.
And why we use that??
Thanks in advance..!!
it is not the view controller that is unloaded when viewDidUnload is called but only its view. The view controller stays alive until it is deallocated.
In viewDidUnload, you have to release those objects that are part of the view and everything that can and will be recreated in viewDidLoad (because viewDidLoad will be called again when the view controller need to recreate its view). This includes all your outlets. You also have to set these variables to nil to avoid overreleasing them.
Because in dealloc, you should release all objects your view controller retains, including those you included in viewDidUnload.
viewDidUnload is used to release anything that you might have made when the view is created - this included things in viewDidLoad but also includes and IBOutlet properties that are created from inside a xib file. these should all be released and set to nil in viewDidUnload.
Anything else should just be released in dealloc.
The idea is that if viewDidUnload is called to free some memory, the view can be recreated again completely from your viewDidLoad method.
Also see this question;
memory management on the iPhone with viewDidUnload
It is good for memory management. If you release objects associated with a View controller when the controller unloads, you effectively reduce the memory footprint of your application. Retaining objects even when you are not using them makes your application more prone to memory warnings & eventual termination.
Hence, it is good practice to allocate in ViewDidLoad & release in ViewDidUnload.
HTH,
Akshay

Managing memory for popover controllers and their content views

When the user taps on the right callout accessory on my map view's pin's callout I am showing a popovercontroller with a view inside it. I am maintaining 2 retained properties in the mapcontroller for this. I am also releasing these properties in dealloc of the mapcontroller - which probably never happens.
When the user deselects the annotation view I want all this memory released, does assigning nil suffice?
- (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view
{
[self.informationViewController.view removeFromSuperview]; //remove from popovercontroller
self.informationViewController = nil;
popoverController = nil;
}
Yes, it should. This sets the reference of the object to nil which in turn releases the object. I'm no expert on memory management though, so if anyone wants to downvote/correct me, feel free.
I believe you also will need to release the objects.
Wouldn't just assigning nil only remove your pointer to the object in memory? I suspect the object would still reside in memory and still have a retain counter assigned to it, so it will not be removed from memory until it's retain count was decremented.
Further, by assigning your pointer to nil before you released the object, I also would suspect that you will have created a memory leak because the attempt to call release in the dealloc of the controller will not actually release the object.
I'm not 100% sure about this, but ... here is also a link to the Memory Management Programming Guide.
Also, for the future, if you want to be sure, you could run your application using the Leaks performance tool, it should show you where you're leaking memory and what objects are currently allocated in memory, etc...
I could try to setup the scenario in a test project real quick and monitor it using Leaks and update my answer later also.

How to completely unload view controller loaded from nib?

I'm writing iPhone application which uses multiple views, each with own controller. They are loaded programmatically using initWithNibName: and released before switching to other controller, so only one view is visible at a time.
After releasing view controller (I have checked that dealloc is called) not all memory is freed. I do release all outlets when deleting controller. Also, setting self.view to nil in controller's dealloc doesn't solve the issue. Memory consumption gets bigger though Leaks from instruments doesn't show any leaks.
Is there any way to completely remove those views with their controllers from memory? I want to have the same free memory amount before new controller is created and after it is deleted.
You can override retain and release on any class, to get a better understanding of when the retain count gets higher than you might have expected.
Something like this:
- (id) retain
{
NSLog(#"Retain: Retain count is now %d", self.retainCount+1);
return [super retain];
}
- (void) release
{
NSLog(#"Release: Retain count is now %d", self.retainCount-1);
[super release];
}
When that is said, I think you have to check that your "memory leak" is not just something the system has cached. If you can consistently use more memory by doing the same sequence again and again, then you have a leak.
Problem is solved now, it was UIWebView. It kept some cache and there was no way to clear it. After replacing UIWebView with UIScrollView (it was used to show simple local page) problem is gone.

iPhone Development - Simulate Memory Warning

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");
}