How to completely unload view controller loaded from nib? - iphone

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.

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 implement didReceiveMemoryWarning?

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

iPhone - insertSubview makes ObjectAlloc jump

Whenever I add a new viewController my ObjectAlloc jumps up really high and never comes back down. Even after calling removeFromSuperview. Is this normal?
if((UIButton *) sender == gameArcadeBtn) {
GameArcade *gameArcadeController = [[GameArcade alloc]
initWithNibName:#"GameArcade" bundle:nil];
self.gameArcade = gameArcadeController;
[gameArcadeController release];
[self.view insertSubview:gameArcadeController.view atIndex:1];
}
Instantiating a view always creates many objects.As long as this view is in memory or has not been autoreleased, the objects will remained alloced in memory. Thus, to answer your question, this is normal.
It sounds like you are worried about memory usage and while it is important to watch the object allocs so that it doesn't get too it is more important to find your app leaks.
Some memory management tips:
1) do lazy loading. Only load your views when the user asks for them, not all at the beginning of the app
2) remove everything that you possibly can when you dont need it anymore. This means doing tons of work in viewWillAppear and viewDidDisappear
3) learn about #properties and how it relates to autoreleasing, and do not use properties for everything.
4) As appealing as it is, avoid autorelease and manually release objects when you dont need them anymore.
that's probably due to the fact that you're still retaining the view's controller in the class. try releasing that