Do we really need a safe release macro?
Quite a lot of people seem to use a macro such as
#define SAFE_RELEASE(X) [X release]; X = nil;
(myself included).
I've been reassessing why I am using it and wanted to canvas some opinion.
The purpose (I think) for using this macro is so that if you were to accidentally use your object after releasing it then开发者_C百科 you won't get a bad access exception because objective-c will quite happily ignore it when the object is nil.
It strikes me that this has the potential to mask some obscure bugs. Maybe it would actually be preferable for the program to crash when you try to use X again. That way during testing you can find the issue and improve the code.
Does this macro encourage lazy programming?
Thoughts?
I think you discuss all the pros and cons in your question, so I don't have a huge amount to add. Personally I don't use the construct. As you suggest, it can be used to paper over areas where people don't understand the memory management correctly. My preference is to fix the bug and not the symptom.
However, one compromise that I have seen from time to time is:
- Make it crash during development
- Do the
var = nil;
in production code
That way it might be more reliable with paying customers and still crashes early during development.
I'm not keen on this either, as you're using different code to your users and just because the buggy version keeps running doesn't mean it's doing the right thing. Not crashing but corrupting your database is not desirable behaviour...
I think it, or an equivalent such as self.myVar = nil where applicable, is a good. There are many cases where you simply can't just assign nil and assume any later access is a bug.
For example in UIKit it's good behavior to free up as many resources as possible when the OS ask. E.g.
- (void)didReceiveMemoryWarning
{
[myCachedData release];
[super didReceiveMemoryWarning];
}
Now, when my class is next used how am I to know that myCachedData is now invalid? The only way (short of having ANOTHER variable acting as a flag) is to set myCachedData to nil after releasing it. And condensing those two tedious lines into one is exactly what SAFE_RELEASE is for.
You don't need it, but it is handy to have. I use something similar in my apps. You can consider it "lazy", but when you have somewhere around 20 objects, writing these out manually gets tedious.
I was looking into the same question. With the bit of reading I did I just have something like this:
#define DEBUGGING
//#define PRODUCTION
#ifdef DEBUGGING
#define SAFE_RELEASE(X) [X release];
#else
#define SAFE_RELEASE(X) [X release]; X = nil;
#endif
So that way if I'm developing, I get the Crashes. In Production I don't.
Scott<-
As Andrew pointed out, there are cases where assigning nil
isn't only avoiding bugs, but necessary. Just consider typical UIViewController
code
- (void)viewDidLoad {
button = [UIButton buttonWithType:UIButtonTypeRoundedRect]; // autoreleased
[button retain]; // keep alive
}
- (void)viewDidUnload { // view has been removed
[button release];
}
- (void)dealloc { // destroying the view controller
[button release]; // <-- problem
}
In case the controller is loaded, later unloaded (because another view controller is displayed a memory is running low) and finally destroyed, the [button release]
in dealloc
would over-release the button (send a message to a released object). Therefore it it's necessary to assign nil. The safe solution would be:
- (void)viewDidUnload { // view has been removed
[button release];
button = nil;
}
- (void)dealloc { // destroying the view controller
[button release]; // <-- safe
}
For those cases a macro is nice and useful. To be more explicit about what it does, better name it RELEASE_AND_NIL
#define RELEASE_AND_NIL(X) [X release]; X = nil;
精彩评论