Why do I have to call super -dealloc last, and not first? - iphone

correct example:
- (void)dealloc {
[viewController release];
[window release];
[super dealloc];
}
wrong example:
- (void)dealloc {
[super dealloc];
[viewController release];
[window release];
}
Althoug in alsmost all other cases when overriding a method I would first call the super's method implementation, in this case apple always calls [super dealloc] in the end. Why?

Its just a guideline. You can call other instructions after [super dealloc]. however you can not access variables of the superclass anymore because they are released when you call [super dealloc]. It is always safe to call the superclass in the last line.
Also KVO and depended (triggered) keys can produce side effects if they are depended of already released member variables.

I don't know anything about programming for the iPhone, but I would assume that it is for the same reason that destructors need to be called in reverse order. You want to make sure that all your 'garbage' is cleaned up before calling your superclass. If you do it the other way around things can get messy. For instance, if your destructor needs to access memory that the super destructor has already freed:
class X {
private Map foo;
function __construct() {
foo = new Map();
}
function __destruct() {
foo.free;
}
}
class Y extends X {
function __construct() {
super.__construct();
map.put("foo", 42);
}
function __destruct() {
super.__destruct();
if (map.containsKey("foo")) { // boooooooooom!
doSomething();
}
}
}
You may not encounter this problem in your code, because "you know what you're doing", but it is a safer and overall better practice not to do such things.

[super dealloc] is freeing up the memory used by your object, including the pointers to viewController and window. Referring to variables after you've freed them is hazardous at best.
See this answer.

Here is actual example where [super dealloc] must be last, otherwise the call to removeFromRunLoop will cause crash. I'm not sure what happens inside NSOutputStream's removeFromRunLoop, but it seems to access 'self' in this case.
Setup:
[outputStream setDelegate:self];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
Dealloc:
- (void)dealloc {
if (outputStream) {
[outputStream close];
[outputStream removeFromRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[outputStream release];
outputStream = nil;
}
delegate = nil;
[super dealloc]; // must be last!
}

You practically almost have [super dealloc] at the end because it frees up the variables of the superclass and they can no longer be accessed.
One exception is if you have a subclass of UITableViewController that is using another class as its table view delegate. In that case you have to release the table view delegate after [super dealloc] because the table view is referencing the table view delegate and the table view has to be released first.

[to the last post] Wouldn't the tableView referencing the delegate be responsible for releasing it's own delegate? I would think it's retained it when set (so you could release or autorelease it) and it would take care of itself?
As for the OP question, I will always call super first if I'm constructing and call super last if I'm destructing. I think of it as "I want to have super build what it wants so I can build on that, and I want super to tear down last after I clean up after myself." Virtually all the calls I use are constructing though, except dealloc, so that's why you would see it last in my dealloc code always.

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.

Strange custom delegate actions

Ok -- this one is weird. I have a singleton class that loads information from an XML file. I am using a delegate definition as follows (I define the delegate in a separate header file to make life easier):
#protocol ResourceClassDelegate <NSObject>
#optional
- (void)picturesDidStartLoading;
- (void)picturesDidFinishLoading;
#end
In the resource file, the delegate is defined correctly (I believe):
#property (assign) id<ResourceClassDelegate> delegate;
When using the delegate, the code in the resource class is as follows:
-(void)refreshPiecesOfHistoryWithOperation {
NSLog(#"Operation Started");
if ([delegate respondsToSelector:#selector(picturesDidStartLoading)])
[delegate picturesDidStartLoading];
self.picturePacks = [HistoryXMLParser loadPicturePacks];
[self.allPiecesOfHistory removeAllObjects];
// now lets put all of them in one big file...
for (PicturePack *pp in self.picturePacks) {
for (int ct = 0; ct < [[pp piecesOfHistory] count] ; ct++) {
[self.allPiecesOfHistory addObject:(PieceOfHistory *)[[pp piecesOfHistory] objectAtIndex:ct]];
}
}
NSLog(#"Operation Ended");
if ([delegate respondsToSelector:#selector(picturesDidFinishLoading)])
[delegate picturesDidFinishLoading];
}
Now... in the class that is listening to the delegate, it is assigned:
- (void)viewDidLoad {
[super viewDidLoad];
// now for the part that makes the loading all happen...
[[ResourceClass sharedResourceClass] setDelegate:self];
}
And in the listening class, the methods are defined....
#pragma mark ResourceClassDelegate
-(void)picturesDidStartLoading {
if (loadingActivity == nil)
loadingActivity = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
[self.view addSubview:loadingActivity];
[loadingActivity setCenter:[self.view center]];
[loadingActivity startAnimating];
}
-(void)picturesDidFinishLoading {
if (loadingActivity != nil) {
[loadingActivity stopAnimating];
[loadingActivity removeFromSuperview];
}
[self.tableView reloadData];
}
Now for the problem... every single time, in the listening class, the method (void)picturesDidFinishLoading is called. The method (void)picturesDidStartLoading never is called.
When I debug the code, in the resource class, the line
if ([delegate respondsToSelector:#selector(picturesDidStartLoading)])
[delegate picturesDidStartLoading];
never reaches the delegate method call - even if I remove the if statement. The line
if ([delegate respondsToSelector:#selector(picturesDidFinishLoading)])
[delegate picturesDidFinishLoading];
is always called.
any ideas?
Ok -- I figured it out....
The delegate was nil during the first call. The reason it is nil is because the function using the delegate was called in the source during the init method. The init method was not complete when the first test of the delegate was performed. At this time the delegate was nil because it is not instantiated until the the init method completes. The reason the second test of the delegate worked is because I submitted the process using an NSOperationQueue.
To fix the problem I have to move things around a bit... it's all about the timing!
Well now that was fun....
That's weird, try to remove #optional in the protocol declaration, and see if you get some warnings.
Try to print a log inside the method as well, other than that it looks fine.

iPhone: [super viewDidUnload] calling order

I would like to know if there is any difference between calling [super viewDidUnload] before realeasing properties or after it.
Thank you!
self.webView = nil;
self.fullText = nil;
[super viewDidUnload];
or
[super viewDidUnload];
self.webView = nil;
self.fullText = nil;
This depends on what the superclass does in viewDidUnload. If it's just a standard UIViewController, either will do because -[UIViewController viewDidUnload] does nothing.
I never thought it mattered, but we just found a bug where the app crashed calling it first. The trend at my office has been to call it last, but the Apple template puts it first.
You can do what you want, but I will always call it last from now on.
This question is not fully answered. (And it's first hit when I google on this topic, so a complete answer would be useful to me and others.)
If the superclass DOES something in viewDidUnload, then what is the correct order for calling it in your class's viewDidUnload: before or after releasing properties?
For the record, it seems the consensus is to call the super method LAST:
-(void)viewDidUnload {
self.some_property = nil;
[super viewDidUnload];
}
This only matters if the superclass of your view actually does something in viewDidLoad. The default UIViewController doesn't do anything in viewDidLoad so if your view is a standard UIViewController then you can put [super viewDidLoad] anywhere in the viewDidLoad method. However, if super does something in viewDidLoad, then it does matter where you put [super viewDidLoad].
This is just like the dealloc method.
- (void)dealloc
{
[array release];
[super dealloc];
}
So I think we should call [super viewDidUnload] last.

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.

Memory Crash due to [super dealloc] of uiview controller

I am having a uiviewcontroller instance and when I am releasing it the dealloc method of it is called.
I have released some objects in dealloc method of that uiviewcontroller.
If I comment [super dealloc] the app is working fine but if don't it is crashing.
I think there is no problem with the releases that I am doing in that method, but if I do [super dealloc] it is crashing.
Can any one help me out with this?
Hard to tell from your post without more information, but does your dealloc method look like this?
- (void)dealloc {
[super dealloc];
self.someProperty = nil;
}
Because if it does, you're calling a setter method on a deallocated instance. You should always call [super dealloc] last:
- (void)dealloc {
self.someProperty = nil;
[super dealloc];
}
Not sure if that helps. Try posting what your dealloc method looks like if not. Hard to troubleshoot in the dark.
It's not possible to help you without more information. The code you described is perfectly fine. The problem is in some other part of your app.
You probably access the view controller after releasing it, so the problem is not the [super dealloc] but any other place in you application that accesses the view controller.
Maybe you are releasing the controller in the wrong place. That could be why your [super dealloc] in your ViewController.m is crashing
You shouldn't called [viewController release] until you want that controller to die. For example, if you have an application with just a viewcontroller you must not release it until the application ends. This is because that controller needs to stay alive all the time to control the view. If you have in your ApplicationDelegate something like this, it will crash:
- (void)applicationDidFinishLaunching:(UIApplication *)application {
UIViewController *controller = [[UIViewController alloc] init];
[window addSubview:controller.view];
[controller release]; //this will crash
}
Instead of that you should place your viewcontroller in the header file (.h) and release it in the dealloc method:
- (void)dealloc {
[controller release];
[window release];
[super dealloc];
}
I hope this helps.