Do we really need a safe release macro? - objective-c

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 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;

Related

Deallocating object while in background thread

I have been working on iOS project, which used data downloading in background. Well, honestly say - implementation wasn't the best one (and was changed later to remove such nonobvious pattern), and I've got some problems with not-deallocated objects, and can't say, that docs gave me a clear understanding of what's going on. Code, demonstrating common idea:
- (void)loadModelAtIndex:(NSUInteger)index {
Model *model = [self modelAtIndex:index];
if (model) {
model.index = index;
[self performSelectorInBackgroundThread:#selector(loadModelInBackgroundThread) withObject:model]
}
}
- (void)loadModelInBackgroundThread:(Model *)model {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
model.data = [NSData dataWithContentsOfURL:model.url];
//and some other changes can be here
[self performSelectorOnMainThread:#selector(modelDidLoad) withObject:model waitUntilDone:NO]'
[pool drain];
}
- (void)modelDidLoad:(Model *)model {
[self saveModel:model atIndex:model.index];
[self loadModelAtIndex:model.index + 1];
}
Well, almost all time it has been working as expected. Except if caller has beed deallocated while downloading in background - Model object stayed in memory, without being released (I've got growing memory at this point).
Can anyone explain me, what will happen in case of deallocation, while background thread is running? I'm not sure this code can be suitable at all, but still interested. Any modification will make things run well?
Except if caller has beed deallocated while downloading in background - Model object stayed in memory
the performSelector | …Thread… calls retain their arguments. If your objects are deallocated in that time, the problem lies elsewhere. Run with zombies enabled to locate it - you can record every reference count.

Reusing NSObjects by Overriding release in Obj-C

I am implementing an object reuse scheme using a singleton class.
What I do basically is:
MyClass* obj = [[MyClassBank sharedBank] getReusableItem];
The bank is just an NSMutableSet tweaked for optimum reusability. When I was happily implementing this Singleton, I had in mind that I will just do the following when I am done with "obj":
[[MyClassBank sharedBank] doneWithItem:obj];
Currently, My code would work if I where to use it this way, but I later realized that I sometimes add "obj" to an "NSCollection", and sometimes I call:
[theCollection removeAllObjects];
At first I thought about making my own class that is composed of a collection, then I would iterate the objects within the collection and call:
[[MyClassBank sharedBank] doneWithItem:obj];
But, that's too much of a hassle, isn't?
A neat idea (I think) popped into my mind, which is to override: -(oneway void)release;, so, I immediately jumped to Apple's documentation, but got stuck with the following:
You would only implement this method to define your own reference-counting scheme. Such implementations should not invoke the inherited method; that is, they should not include a release message to super.
Ao, I was reluctant to do that idea .. basically:
-(oneway void)release{
if ([self retainCount] == 1) {
//This will increment retain count by adding self to the collection.
[[MyClassBank sharedBank] doneWithItem:self];
}
[super release];
}
Is it safe to do that?
PS: Sorry for the long post, I want the whole idea to be clear..
EDIT:
How about overriding alloc alltogther and adding [[MyClassBank sharedBank] getReusableItem]; there?
Suggested method:
You're playing with the reference counting system. 99.9999999999999999% of the time this is a bad idea. I would highly recommend going with a different mechanism. Perhaps these objects could implement their own reference count that's independent of the retainCount? Then you could use that referenceCount to actually control when an object is ready to be re-used or not.
Not suggested method:
If, for some weird reason, you can't do that, then you could do the following thing that is still a bad idea and that i don't recommend you actually use:
You can override dealloc:
- (void)dealloc {
[ivar release], ivar = nil;
[anotherIvar release], anotherIvar = nil;
somePrimitive = 0;
// do not call [super dealloc]
}
- (void)_reallyDealloc {
[self dealloc]; // clean up any ivars declared at this level
[super dealloc]; // then continue on up the chain
}
Basically, the dealloc method would be the point at which the object is ready for re-use. When you're totally done with the object and finally want it to go away, you can use the _reallyDealloc method to continue on up the chain, eventually resulting in the object getting freed.
PLEASE don't do this. With things like Automatic Reference Counting, this is going to introduce you into a world of hurt and really bizarre debugging scenarios. A lot of the tools and classes and stuff depend on the reference counting mechanism to be working without alteration, so screwing around with it is usually not a Good Idea™.
For ppl who find this approach interesting/useful, Here is a cleaner way than calling [super dealloc]; directly (which is definitely bad)
//BAD!
//-(void)dealloc{
// for some reason, the retainCount at this point == 1
// if (![[BankStep sharedBank] purgeFlag]) {
// [self resetObject];
// [[BankStep sharedBank] doneWithItem:self];
// } else {
// [children release];
// [super dealloc];
// }
//}
by calling [[Bank sharedBank] purgeBank]; , set the flag to true, then remove all objects from the NSSet.
Adapted solution:
#Joe Osborn idea of using categories to implement a returnToBank Method!

Why can't my singleton class return a value that will stay in scope

Stick with me. I'm visually impaired, have never used this site before, and will probably not post this in precisely the format that you are all used to. I apologize for any unintentional faux pas's herein.
Using Objective-C in an iOS project…
I have a singleton class, set up in what appears to be the usual way for Objective-C. It is, in the main, a series of methods which accept NSString values, interprets them, and return something else. In the code below, I'm simplifying things to the barest minimum, to emphasize the problem I am having.
From the singleton class:
- (NSUInteger) assignControlState:(NSString *)state {
// excerpted for clarity...
return UIControlStateNormal; // an example of what might be returned
}
Now, an instance of another class tries to use this method like so:
- (void) buttonSetup:(UIButton*)button {
[button setTitle:#"something" forState:[[SingletonClass accessToInstance] assignControlState:#"normal"]];
}
This code actually works. HOwever, when the system goes to draw the UI which includes the button whose title was set in this way, an EXC_BAD_ACCESS error occurs.
If the assignControlState method is moved into the same class as the buttonSetup method, no error is generated.
I'm guessing this is something about Apple's memory management that I'm not fully understanding, and how things go in and out of scope, but for the life of me, I can't figure out where I'm going wrong.
HOpe someone can help. Thanks.
The problem is in your accessToInstance method. I'll bet you are under-retaining. The implementation should be more like this:
static SingletonClass *sSingletonClass = nil;
#implementation
+ (id)accessToInstance {
if (sSingletonClass == nil) {
sSingletonClass = [[[self class] alloc] init];
}
return sSingletonClass;
}
#end
Now, if your program is following normal memory management rules, the singleton will stay around. You can check by writing:
- (void)dealloc {
[super dealloc]; // <-- set a breakpoint here.
}
If the debugger ever stops at this breakpoint, you know something in your program has over-released the singleton.
You know that bit you excerpted for clarity? I think you need to show us what it is because there's probably an over release in it somewhere.
Specifically, I think you release an autoreleased object. If you do that and don't use the object again, everything will carry on normally until the autorelease pool gets drained. The autorelease pool gets drained automatically at the end of the event at about the same time as the drawing normally occurs.
That would also explain the delayed crash following the NSLogs.

Leaks reports seemingly unrelated issues

I'm fairly new to Cocoa and Objective-C. Currently I'm developing a fairly basic application to test my knowledge and put some of the stuff I've been reading about into practice. Everything is working, but Leaks reports a number of issues.
None of these leaks seems to be directly applicable to code that I've written (I have read and tried to follow Apple's rules on memory allocation). Currently my project makes use of Garbage Collection and I'm developing on Snow Leopard. Running AnalysisTool finds no issues with my code (aside from a few naming convention warnings).
Currently my application makes use of an NSTableView which I have hooked up to an NSArrayController. Interacting with the NSTableView seems to cause leaks to report issues (actions such as sorting table columns and other standard user interaction). This leads me to believe that my use of the NSArrayController (and my implementation of its content source) is to blame.
Currently the NSArrayController receives its content from an NSMutableArray (timers) handled in my Application's delegate like so:
- (id) init
{
if (self = [super init])
{
timers = [NSMutableArray array];
}
return self;
}
- (void) dealloc
{
[timers release];
[super dealloc];
}
Within Interface Builder my NSArrayController has its Object Controller set to the Timing class, which is defined below:
#interface Timing : NSObject {
NSString *desc;
NSDate *timestamp;
bool active;
}
#end
#implementation Timing
-(id) init
{
if (self = [super init])
{
desc = #"New timing";
timestamp = [[NSDate alloc] init];
active = false;
}
return self;
}
-(void) dealloc
{
[timestamp release];
[super dealloc];
}
#end
I've used standard Cocoa bindings to hook up Add and Remove buttons to manipulate the TableView and these seem to work correctly (clicking Add will create a row in the TableView with the value of 'New timing', for instance).
Leaks reports that the libraries responsible are AppKit and CoreGraphics. Although, honestly, I'm still new to the Leaks tool - so I could be reading its output incorrectly. If it helps, I've placed a screenshot of its output here. If anyone could point me in the right direction, that would really be appreciated.
As an aside, I've also been experimenting with manually adding objects to the timers array without the use of Cocoa bindings. Here's what I came up with:
Timing *timingInstance = [[Timing alloc] init];
[timers addObject:timingInstance];
[timingInstance release];
[timersController setContent:timers];
[timersTableView reloadData];
Again, this seems to work, but I thought it best to ask the experts!
Your memory management for the timers array is not quite correct. Using the array factory method will return an instance of NSMutableArray that has already been autoreleased, so the lifetime of that object is (probably) limited to the end of the current run loop, and it will be over-released when you call release in your dealloc method. The proper way to do it is as follows:
- (id) init
{
if (self = [super init])
{
timers = [[NSMutableArray alloc] initWithCapacity:0];
}
return self;
}
This method will give you an instance of NSMutableArray with a retain count of 1, which will then drop to zero (and properly release the memory) when you call release in your dealloc method. The call to alloc in your init method is balanced out by the call to release in your dealloc method. I notice that this is the exact pattern that you used for your NSDate object in the Timing class, so you are already familiar with the idea.
Your code as written is not leaking. The Cocoa frameworks will sometimes generate false leak reports when run under leaks, as certain things such as singletons and caches which are used in the implementation of the frameworks will sometimes show up as leaks even though they're not.
You're probably better off running the ObjectAlloc and/or ObjectGraph instruments to get an idea of when your objects are being allocated and deallocated.
Currently my project makes use of
Garbage Collection and I'm developing
on Snow Leopard
I don't understand. You're using garbage collection right? If so then GC takes care of releasing objects for you so anywhere you use "release" does absolutely nothing. Release calls are ignored when GC is enabled. You only use release when you're managing the memory yourself which is when GC is off. Also, your dealloc methods do nothing too. In GC that method is never used. When you create an object and then you finish with it you tell GC that it's OK to get rid of the object by setting it to nil. That's all you have to do. There is no "release" needed or dealloc method needed. Just set things to nil or or not as needed.

Leak in NSScanner category method

I created an NSScanner category method that shows a leak in instruments.
- (BOOL)scanBetweenPrefix:(NSString *)prefix
andSuffix:(NSString *)suffix
intoString:(NSString **)value
{
NSCharacterSet *charactersToBeSkipped = [self charactersToBeSkipped];
[self setCharactersToBeSkipped:nil];
BOOL result = NO;
// find the prefix; the scanString method below fails if you don't do this
if (![self scanUpToString:prefix intoString:nil])
{
MY_LOG(#"Prefix %# is missing.", prefix);
return result;
}
//scan the prefix and discard
[self scanString:prefix intoString:nil];
// scan the important part and save it
if ([self scanUpToString:suffix intoString:value]) // this line leaks
{
result = YES;
}
[self setCharactersToBeSkipped:charactersToBeSkipped];
return result;
}
I figure it's the way I'm passing the value to/from the method, but I'm not sure. It's a small leak (32 bytes), but I'd like to do this right if I can. Thanks in advance.
I found the answer. I had a model class that used the result of the code above, but forgot to release the property in the dealloc method. I should have caught it with Instruments, but I didn't know where to look. I got caught up in the call stack in the Extended Details, which had only part of the info I needed.
For morons like me, here's what I did:
Run your app with Instruments ... Leaks.
In Instruments, watch the leaked blocks view (the grid icon at the bottom) and turn on the extended detail.
If you have multiple leaked objects, click the disclosure triangle so you can look at a discrete address.
Next to an address, there will be a detail arrow. Click on it.
Now you'll be looking at history. It starts with Malloc and shows you each retain and release.
You should have a release for the malloc and a release for every retain in the history. Match up your retains and releases and look for the oddball. When in doubt, look toward the bottom of the page and carefully review any properties you may have forgotten to release in your dealloc method(s).
OK, I'm stupid. This method does not leak. The leak is farther down my call stack, but I can't figure out where. Sorry for the premature post. I'll follow up when I figure out where the real leak is, if I can't figure out the fix myself.
In answer to Yuji's comment, my method call is:
NSString *title;
[fileScanner scanBetweenPrefix:kTitlePrefix
andSuffix:kTitleSuffix
intoString:&title];
Is it the &title that's the problem? I patterned it after the existing NSScanner methods and calls.