iphone - question about over-releasing - iphone

In any iphone template UIViewController class you will see this:
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
But if I set self.myOutlet = nil in viewDidUnload, and I also have [self.myOutlet release] in the dealloc method, isn't that over-releasing self.myOutlet, since setting it to nil will already set its retain count to zero?
If I don't release it in dealloc(), Leaks reports a memory leak because it never sees self.myOutlet getting released, assuming I have something like this in the header file:
#property (nonatomic, retain) UIView *myOutlet;

If you set self.myOutlet to nil in viewDidUnload, the call in dealloc to [myOutlet release] will simply send a release message to nil. It's perfectly acceptable to send messages to nil. Nothing bad will happen and you won't over-release anything.
The thing to watch out for is releasing the object referenced by myOutlet in viewDidUnload without setting myOutlet to nil. In this case, you'll get a crash in dealloc. Why? Because myOutlet will still be referring to the memory location once occupied by the myOutlet object, and your dealloc method will try to send whatever is currently at that memory location a release message.

-(void)viewDidUnload is called when a memory warning is issued, -(void)dealloc is called in the complete destruction of a view (these aren't the same). Therefore, you should be doing both
- (void)viewDidUnload {
// Release any retained subviews of the main view.
self.myOutlet = nil;
}
- (void)dealloc {
[self.myOutlet release];
}
Edit: What James said furthers the point.
Cheers!

It's not the variable that has a retain count; it's the object. When you do self.something = nil, it does indeed release the object — and it sets the variable to point to nil. So any further release messages sent to self.something do not go to the original object. Instead, they go to the new value, nil, which happily ignores anything you tell it.

Related

Releasing a UIView

NB: To clarify what I'm trying to do here:
I have an instance of a subclass of UIView. When this instance gets released from a View Controller I would like that the dealloc method of that instance be called.
The point being to release other objects within that instance of the subclass of UIView.
In the View Controller:
- (void)viewDidLoad
{
[super viewDidLoad];
self.mav = [[MapAreaView alloc]init];
[self.view addSubview:self.mav];
[self.mav release];
t_ = [NSTimer scheduledTimerWithTimeInterval: 20.0f target: self selector:#selector(onTick) userInfo: nil repeats:NO];
}
and:
- (void)onTick
{
NSLog(#"Releasing...");
[t_ invalidate];
t_ = nil;
[self.mav release];
[self.mav release];
[self.mav release];
NSLog(#"Done releasing.");
}
In MapAreaView:
- (void)dealloc
{
NSLog(#"map view area dealloc called.");
[super dealloc];
}
I am trying to release the MapAreaView and have it's dealloc method called.
Also, when I run this app, Releasing... and Done releasing. get printed, but not map view area dealloc called. And despite all of my excessive release messages sent to self.mav the app doesn't crash. Weird.
Ideas?
EDIT 1
#interface MemoryManagmentViewController : UIViewController {
MapAreaView *mav_;
NSTimer *t_;
}
#property (nonatomic, retain) MapAreaView *mav;
which is then synthesized: #synthesize mav = mav_;
EDIT 2
Please not that the timer is not for use in my real application. I'm just using it to learn about memory management.
First off, don't use [self.mav release]. This is incorrect use of properties, and is not guaranteed to release your object. Use self.mav = nil instead.
Secondly, I presume your crazy timer is there just to try and force the release of your view and isn't really code you are using? Because it really shouldn't be there!
To manage the memory of a view that you want to both keep a pointer to and add as a subview to your main view, remove the release and timer messages from your viewDidLoad. You now have one retained pointer to mav, via your property, and the view will be retaining it as well since it is a subview.
In viewDidUnload, your view no longer exists so it will no longer have a pointer to mav. Therefore, if you put self.mav = nil there, mav will be released.
You shouldnt try to release it while it is still a subview of another view.
You don't get to choose when dealloc gets run*
You don't know what else in the iOS framework is retaining your map view.
You should think about memory management in terms of ownership - while you want an object, make sure you've retained it (either explicitly or as a property) and when you are finished with it, make sure you release it (etiher by calling release, autorelease or by setting your property to nil).
Your map view will get released eventually, don't try to force it!
(And it's been said in many other answers but it pretty important so here it is again - don't call self.property release, do self.property = nil; instead :)
*not entirely true for object in libraries you have written yourself but it's definitely true for objects from third party frameworks, including ones from Apple!
The line:
[self.view addSubview:self.mav];
will retain self.mav and should be paired by this line in onTick.
[self.mav removeFromSuperview];
I'm not sure why your over-releasing in onTick doesn't release self.mav anyway - but if you do weird stuff you can guarantee that weird stuff will happen.
Don't release it within onTick again. Do these two steps:
[self.mavview removeFromSuperView];
self.mavview = nil;
That's it. There won't be any more references, so it will be deallocated.
First and foremost thing is
NEVER call release on self.iVar
Donot user self.iVar while allocating.
These are the basic things in memory management.

How composite objects are released when the NSMutableArray that contains those objects are released?

#implementation TestClass
- (id) init
{
string = [[NSString alloc] init];
return self;
}
- (void) release
{
[string release];
}
#end
#implementation MainScreen
- (void) addItems
{
[myMutableArray addObject:[[[TestClass alloc] init] autorelease]];
}
- (void) release
{
[myMutableArray release];
}
My Question is when we release the myMutableArray, will it call the release method of TestClass or not?
No, that's not how Cocoa memory management works. When you add the object to the array, the array expresses that it owns the object by sending the -retain message. The array keeps this ownership until it itself disappears (i.e. is deallocated), then it no longer needs the object and sends it -release to relinquish ownership. The array does not need to retain or release the objects every time it is retained or released.
To summarise: objects retain other objects when they need to take ownership of them, and release them when they no longer need that ownership.
This points to your memory management of the string ivar being incorrect. You correctly take ownership of a zero-length string in -init (where I'm using "correctly" in a very loose sense), but then every time your object is released it releases the string. Consider:
TestClass *obj = [[TestClass alloc] init];
[obj retain];
[obj release];
[obj release];
the above is likely to crash (and if it doesn't, you're very unlucky). You should release the string in -dealloc, when your object finally doesn't need it any longer. If you change the object referred to by the ivar, you also have to change the ownership.
Never ever override -release! What you want to override is -dealloc! And in there you have to call [super dealloc]
Please read http://developer.apple.com/library/mac/documentation/cocoa/conceptual/memorymgmt/MemoryMgmt.pdf
And to your question, if your array gets deallocated, every object in the array is sent a release message.
You should be overwriting the - (void) dealloc method, not the -(void) release one; and always call [super dealloc] at the end.
Yes, releasing an NSMutableArray releases the individual objects too.
just as
[myMutableArray addObject:[[[TestClass alloc] init] autorelease]];
will increment retainCount to the TestClass instance
so will release decrement the retainCount for all objects in the array
note that dealloc is the method to implement in your custom class, it is called when your object is released.

UIActivityIndicatorView nillify makes it crash

For the code below if I nillify the indicator by "self" it crashes if dont use self than no crash.
Isnt it the rule that I should always use "self" to access ivars created and retained by #property ?
#property(nonatomic,retain) UIActivityIndicatorView* activityIndicator;
if(activityIndicator!=nil){
[activityIndicator removeFromSuperview];
//self.activityIndicator = nil; //crashes!
activityIndicator = nil; //does not crash
}
Generally speaking:
self.activityIndicator = nil; //crashes!
will release the activityIndicator, so this is possibly related to the crash.
activityIndicator = nil; //does not crash
will not release the activity indicator, you don't have a crash, you have a memory leak.
In your concrete case, possibly the crash depends on the fact that when you execute this:
[activityIndicator removeFromSuperview];
the activity indicator is released; now, if it also happens that retain count goes to 0, the object is also deallocated, but the property is not updated to reflect the fact that the object has been deallocated. SO, when you set it to nil, the setter tries to release it, but the obejct dos not exist anymore and hence the crash.
This is a guess. It should not happen if you retained correctly the activity indicator in your class. So, either you review the code where you create the activity indicator and the places in your code where you use activityIndicator or you post it for more help...

iPhone: Calling dealloc on parentViewController causes an exception

I'm dealing with viewDidUnload and dealloc methods and I've founded a problem when calling [super dealloc]; in parent view controller.
I have a lot of view controllers with custom code which I have putted outside on a parent view controller. So, when defining my view controllers I set a reference to the super class:
#interface LoginViewController : AbstractViewController
Then, at the dealloc method I call the AbstractViewController dealloc method:
//(Login View Controller code)
- (void)dealloc {
[user release];
[passwd release];
[super dealloc];
}
[super dealloc] execute the following code:
//(Abstract View Controller code)
- (void)dealloc {
[dbUtils release];
[loadingView release];
[super dealloc];
}
If I simulate a memory warning on iPhone Simulator, the following exception is thrown:
2010-03-03 11:27:45.805 MyApp[71563:40b] Received simulated memory warning.
2010-03-03 11:27:45.808 MyApp[71563:40b] *** -[LoginViewController isViewLoaded]: message sent to deallocated instance 0x13b51b0
kill
quit
However, if I comment the [super dealloc] line in AbstractViewController the exception is not thrown and my app still running.
Thank you for your help once again!
It looks like you're freeing your view controller but then trying to use it again. When you free your view controller because of the memory warning, remember to set the pointer to nil so you don't accidentally use it again. i.e. something like
[myLoginViewController release]; //!< Triggers the dealloc call
myLoginController = nil; //!< Makes sure we never use it again
or, if myLoginViewController is a property you can do this in a neater way :
self.myLoginViewController = nil;
Hope that helps,
Sam
Notice -didReceiveMemoryWarning does not trigger -dealloc, it triggers -viewDidUnload. So I guess it is your implementation of -viewDidUnload that does something wrong that causes the controller's final retention to be released so -dealloc is called. I've just encountered this problem in my code which is caused by a retain recycle that is released in -viewDidUnload.
This happened to me and took a while to figure out. One of the delegates was trying to send a message because the delegate was self and it was a strong reference. when I made it weak it seems to have fixed it.

Do I need to set these variables to nil in viewDidLoad as I have in this code?

- (void)viewDidUnload {
self.GPSArray = nil;
self.accelerometerArray = nil;
self.headingArray = nil;
self.managedObjectContext = nil;
self.locationManager = nil;
self.pointLabel = nil;
self.accelerometerLabel= nil;
self.headingLabel= nil;
self.startStop = nil;
self.lastAccelerometerReading = nil;
self.lastGPSReading = nil;
self.lastHeadingReading = nil;
}
- (void)dealloc {
[GPSArray release];
[accelerometerArray release];
[headingArray release];
[managedObjectContext release];
[locationManager release];
[pointLabel release];
[accelerometerLabel release];
[headingLabel release];
[startStop release];
[lastAccelerometerReading release];
[lastGPSReading release];
[lastHeadingReading release];
[super dealloc];
}
The reason viewDidUnload is called, is that your view is being released and any view resources should be freed.
So, you only need to free view related items.
In your case it looks like you'd only need to free the UILabels that are probably in your view. If they were marked as IBOutlets and not in assign properties, you'd want to release the memory used by them:
self.pointLabel = nil;
self.accelerometerLabel= nil;
self.headingLabel= nil;
That also means, that in viewDidLoad if you are setting up the other properties you want to make sure they are not being allocated again if they are there already as it can be called again if the view is unloaded and then reloaded again.
The reason this would be called is if the view controller received a memory warning. You can test this memory warning in the simulator to see how viewDidUnload and viewDidLoad are called.
You don't have to, and if you run this, you'll be wasting some substantial execution time. Using the property method to set a property to nil is the same thing as releasing the property, with the caveat that some extra stuff may or may not happen, depending on how you've set up the setter methods.
So let's walk through this code. At the end of your viewDidUnload method, all of your properties are now nil. The object is then deallocated, and your object attempts to release a dozen nil objects or so. Now, the Objective-C runtime is pretty smart, and if you send a message to nil, (surprise surprise) nothing will happen.
So you've basically got a dozen lines that do absolutely nothing.
no, you shouldn't.
if you do as if the above code, you are wasting effort.
By setting them to nil in viewDidUnload, they will be going thru a process of release and retain automatically, which means, by the time the code get to dealloc, they are actually released and nil, and you are doing another release there for the nil.
Releasing nil object might be erratic.
So, ignore those in viewDidUnload.