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.
Related
I am a former java programmer, and I am having some troubles managing the memory on cocoa touch.
In fact, I think I got the retain/release trick, but still I am not sure I got it right.
For example, I am creating and adding a subview to the main window:
aViewController=[[AViewController alloc]init];//aViewController is (nonatimic,assign), so retaincount = 1 after this line?
[self.window addsubview aViewController];
[aViewController release];//retaincount=0?
And in aViewController I have an IBAction:
[self.view removeFromSuperView];
How can I be sure the object aViewController gets completely 'deleted' and memory released after I removed it from superview (think that controller as a graphic-heavy view controller)?
Also, generally, is there a way to be sure an object is deallocated? I am aware that if I ask ownership of an object I have to release it at a certain point, but what if I just want the object's pointer to be null at a certain point(not basing on the retaincount)? Should I call dealloc directly? I find sometimes very confusing to keep under control the retain/release mechanism.
If someone could give me a quick breakdown to make my mind 'click', i would be extremely grateful.
Thanks.
The short answer is you shouldn't worry about when an object gets deallocated (unless you are debugging a memory management problem). You should just worry about ensuring that if your code retains, copies or inits an object, it releases or autoreleases it. By doing so you will ensure reference counts are properly maintained and hence deallocation will be managed for you.
Leave the task of deciding when to dealloc an object to the runtime. Never call dealloc directly unless you are calling the super classes dealloc method at the end of your objects dealloc method.
Also, don't even look at the retain count property of an object. Various pieces of the framework manipulate those too during the lifetime of the object, and you'll see that number move around seemingly at random. It'll just drive you nuts.
The really important thing is to make sure you've got the objects retained that would be a problem if they went away suddenly, and released when you're okay with them going away suddenly.
aViewController=[[AViewController alloc]init];retainCount is 1
[self.window addsubview aViewController.view];retainCount is 2 (adding the view increments the retainCount)
[aViewController release];retain count decrements to 1;
[aViewController removeFromSuperView];retain count decrements to 0;
Now the dealloc method will be called the allocated memory will be freed. This is what have understood please correct me if i am wrong i always find difficulties during memory management.
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 ;-)
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.
I have an instance variable *TangramLevel(:UIView) currentLevel; in my viewController class, and I have an instance allocated at start (it's retainCount 1). I attached it to the view [self.view addSubview:currentLevel]; (retainCount 2).
When the level finishes, it notifies the controller, so controller removes it from the view [currentLevel removeFromSuperview]; (retainCount 1), and release the allocated instance - [currentLevel release]; -, to have it deallocated (retainCount 0 = invoke dealloc).
Then on the next line, controller wants to allocate/addSubview a new level instance with another level data, but the application crashes (EXEC BAD ACCESS).
If I don't release currentLevel after removeFromSuperview, the appliaction works, but I have an unused level instance left in memory then, which is also a problem (the main problem itself).
Is there any bug in the method I wrote above? Or the bug is elsewhere, maybe in the level class? I allocated some UIImageView in the level, but I release every allocated object in the levels dealloc method. Any ideas?
Post your code.
This is definitely a memory management issue. The question is "where is the problem created?" To answer that, we need to examine the following:
Your "currentLevel" iVar handling code (do you used synthesized properties, etc.). Post it.
How are you assigning the view to currentLevel?
Where are you releasing this, specifically?
How is your view's dealloc implemented (what do you release and how)?
Is there any other code that retains/releases this view or anything related to it?
The fact that you're calling release in your "I'm done with this level, let's swap in the next" code suggests an overall design issue. Make the memory management of each of a class's iVars the responsibility of its accessors and ONLY use the accessors to interact with it (even from within the class/instance). With synthesized properties, this makes it brain-dead-simple. That way you don't have to worry about where to retain/release iVars because it's always funneled through the accessors.
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