views:

1130

answers:

5

Somewhere I was reading that - regarding low memory warnings and giving up an non-visible view with all it's subviews (= a whole nib, I think), you should do that:

-(void)dealloc {
    [anView release], anView = nil;
    [someImageView release], someImageView = nil;

    [super dealloc];
}

rather than

-(void)dealloc {
    [anView release];
    [someImageView release];

    [super dealloc];
}

What's the reason for grounding those pointers to nil (= "no object"), after I call release? Let me guess: Some other method could have -retain'ed the view for some reason (anyone any example for when this could happen?), then the didReceiveMemoryWarning thing happens, and you release a whole nib+view that's currently not visible (i.e. in a multiview-app). As soon as the user wants to see that view again, you would quickly load the nib again and then: It loads all views, connects the outlets, and BANG! Your other retain'ed view's are hanging now without any pointer somewhere lonely in the memory brick, causing a fat and deep memory leak until your app crashes.

Right/Wrong?

+13  A: 

The principle is more general than UIView. indeed it is more general than Objective-C/Cocoa -release method. It is valid also with C malloc()/free() memory functions.

When you no longer need an object or any memory zone, first you release/free it. Then, to make sure that you won't use it again, you clear the means to access this object or memory zone by assigning a nil to an object or a NULL to a memory pointer.

mouviciel
+1. The main point is that dereferencing a null pointer aborts the execution immediately, whereas deferencing a pointer to already freed memory can ‘work’ for some time and leads to unpleasant bugs.
zoul
Thanks. I'm wondering why we see that pattern so rarely in code examples. I've never seen it in Apple's code. But sounds reasonable to me to do that, and I guess it won't hurt anyway.
Thanks
Sample code rarely follows best practices. the goal of most sample code is to demonstrate a principle, api or method as concisely as possible.
m4rkk
Except sending messages to nil is perfectly OK, so you don't get an error. Assigning nil doesn't help.
David Kanarek
Sometimes, you may check if an object has been created with `if (obj==nil) [[obj alloc] init];`. In that case, it is useful to assign `nil`.
mouviciel
+9  A: 

Some other method could have -retain'ed the view for some reason

Unless you're invoking dealloc yourself, it's only called when the retain count becomes zero.

Note that in Objective-C sending a message to a nil "object" is (often) perfectly fine. Doing so will not make your program halt, but the message is simply ignored. However, you cannot send a message to a freed object, which would yield a crash.

So, the following would give you an error:

[anView release];
[anView doSomething];

But, this is in fact ok:

[anView release];
anView = nil;
[anView doSomething];

It's a matter of taste, but for the above, you might in fact prefer to crash your program, rather than wondering why doSomething is not executed...

See also Sending Messages to nil from Apple's Introduction to The Objective-C 2.0 Programming Language.

Arjan
+2  A: 

I use this pattern a lot:

- (void) showHelp: (id) sender
{
    if (helpController == nil)
    {
     helpController = [[HelpController alloc] initWithNibName: @"Help" bundle: [NSBundle mainBundle]];
    }
    [self presentModalViewController: helpController animated: YES]; 
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
    // Release anything that's not essential, such as cached data
    [helpController release];
    helpController = nil;
}

Pretty much everywhere that I allocate a viewcontroller that is modal, or otherwise "temporary". This way, it hangs around if I need it again, but goes away if memory gets low.

Mark Bessey
+4  A: 

The -dealloc method is called when the object is freed and no other methods on the object will be executed after. Therefore, setting any instance variable to nil has no effect outside that object.

If you were releasing an object (without using a setter) somewhere else in the class, it would be important to set the instance variable to nil to prevent code elsewhere from sending a message to that address.

retainCount
"no other methods on the object will be executed after" -- +1 for that clear explanation of the obvious. Thanks! (As for "important to set the instance variable to nil to prevent code elsewhere from sending a message to that address", I wonder why ignoring an unintended message is to be preferred over getting an error message...)
Arjan
Because all programs contain bugs - even well tested ones released to the customer. The kinds of errors generated by sending messages to nil (basically a no-op) are usually less severe than errors generated by sending a message to a freed object. The former usually result in logic errors where the program does not work exactly as intended, the latter will result in a crash. During development the crashes are welcome as they allow the bug to be found - not so for released products.
m4rkk
A: 

rather than doing the expicit release and set to nil, if your accessors have properties associated with them yoc and do the following as a more concise method:

- (void) dealloc
{
    self.retainedProperty1 = nil;
    self.retainedProperty2 = nil;
    self.copiedProperty = nil;
    self.assignedProperty = nil;
}

this way you can have code that has less repetition since the synthesized code will take care of your releases for you.

Edit: i should point out that your properties can't be readonly or else you get compiler errors for obvious reasons :)

Kevlar
No. This is *explicitly* prohibited by Apple, and a good way to cause a crash in some circumstances. See http://stackoverflow.com/questions/192721/why-shouldnt-i-use-obective-c-2-0-accessors-in-init-dealloc
Adam Ernst
That link brings up a good point, but is it still a bad idea if you only use it for readwrite properties that are fully synthesized? (meaning you don't declare synthesize for a property and still override the getter or setter)
Kevlar
It's probably fine, for now, with fully synthesized properties. But Apple doesn't guarantee it's allowed and recommends against it, so this could break in any future SDK.
Adam Ernst