When to release an instance variable - objective-c

Basically I have this scenario going on:
//in interface header
#property(nonatomic,retain)OtherClass *otherClass;
//implementation
- (id)initWithOtherClassInstance:(OtherClass*)otherClass
{
if (self != [super init])
return self;
self.otherClass = otherClass;
return self;
}
- (void)dealloc
{
//Do I need to release otherClass ?
[otherClass release];
[super dealloc];
}
I'm wondering whether I should release an instance variable on which not explicitly alloc, new or copy was called? The memory management guides say I shoud not, but what I'm worrying about is that self.otherClass = otherClass would retain the instance variable and thus cause a leak when I would decide to not release it in the dealloc method.
Moreover releasing the instance variable in the dealloc method does not generate an exception, which it would in case it was overreleased.
Does my reasoning here make any sense, and what is the best thing to do in a case like this ?

Yes you do need to release this, as other answers suggest. But I find that explicitly calling [foo release] on an ivar that you retained via property setter to be a little unbalanced. I prefer setting self.otherClass = nil; in these scenarios.
Of course under the hood it will do a release for you, but it just looks more balanced and clean.

You are doing this right, the rule you mentioned is the 'create' rule. You still need to match all your retains with releases as well.

Your init method is wrong. You need to assign the result of [super init] to self.
Other than that, assuming that self.otherClass is a retain property, what you have done is sort of OK. If you insist on using the property in -init you should assign the property to nil in dealloc, as Ben says, because then whether the property is assign, retain or copy, the right thing will happen.
However,
it is recommended that you do not use accessors in the -init and -dealloc methods. This is because subclasses may override them to do things you don't expect and KVO observers might get notified in dealloc. So you should probably just set and retain the ivar in init and release it in dealloc.

Note that
self.otherClass = otherClass
is the same as
[self setOtherClass:otherClass]
The default implementation on setOtherClass: looks like
- (void) setOtherClass:(OtherClass*)other
{
[other retain];
[otherClass release];
otherClass = other;
}
As you can see, it retains the object, so you have to release it somewhere.
If you don't like explicit release without explicit alloc, new or copy, then you can do the next in dealloc:
- (void) dealloc
{
[self setOtherClass:nil];
[super dealloc];
}

Related

Under Automatic Reference Counting (ARC), where do I put my free() statements?

In cocoa, ARC frees you of having to worry about retain, release, autorelease, etc. It also prohibits calling [super dealloc]. A -(void) dealloc method is allowed, but I'm not sure if/when it's called.
I get how this is all great for objects, etc., but where do I put the free() that matches the malloc() I did in -(id) init ?
Example:
#implementation SomeObject
- (id) initWithSize: (Vertex3Di) theSize
{
self = [super init];
if (self)
{
size = theSize;
filled = malloc(size.x * size.y * size.z);
if (filled == nil)
{
//* TODO: handle error
self = nil;
}
}
return self;
}
- (void) dealloc // does this ever get called? If so, at the normal time, like I expect?
{
if (filled)
free(filled); // is this the right way to do this?
// [super dealloc]; // This is certainly not allowed in ARC!
}
You are right, you have to implement dealloc and call free inside of it. dealloc will be called when the object is deallocated as before ARC. Also, you can't call [super dealloc]; as this will be done automatically.
Finally, note that you can use NSData to allocate the memory for filled:
self.filledData = [NSMutableData dataWithLength:size.x * size.y * size.z];
self.filled = [self.filledData mutableBytes];
When you do this, you don't have to explicitly free the memory as it will be done automatically when the object and consequently filledData are deallocated.
Yes, you put it in -dealloc just like you do under MRR. The only difference with -dealloc is you must not call [super dealloc]. Other than that, it's exactly the same, and will get called when the object is fully released.
As an aside, free() will accept the NULL pointer and do nothing, so you don't actually need that conditional in -dealloc. You could just say
- (void)dealloc {
free(filled);
}

Does dealloc get called after viewDidUnload?

A quick question, after viewDidUnload does the dealloc also get called? I am asking with regards to pickerData, it was my understanding that the variable would be released when the dealloc gets called. My reason for asking is that I have noticed in one book that the author sets pickerData to nil in the viewDidUnload. Is this harmless overkill, maybe even good practice, or is there no scenario where one would not be called without the other.
INTERFACE:
#interface SingleViewController : UIViewController {
NSArray *pickerData;
}
#property(nonatomic, retain) NSArray *pickerData;
#end
IMPLMENTATION:
-(void)viewDidUnload {
[self setSinglePicker:nil];
[self setPickerData:nil];
[super viewDidUnload];
}
-(void)dealloc {
NSLog(#"Here");
[singlePicker release];
[pickerData release];
[super dealloc];
}
#end
gary
No, viewDidUnload: is called when a UIViewController's view is released. dealloc: is only called when the UIViewController's reference count goes to zero. The author's code is good practice.
The author is using synthesized methods to set the ivars to nil, which means those ivars are sent release messages. viewDidUnload: is where you're supposed to release any objects or memory you can easily recreate. The author is essentially saying, "I don't need references to these things anymore, decrement the retain count and hopefully that will free up some memory. I'll recreate it later if necessary in viewDidLoad:."
Setting the ivars to nil will have no consequences if dealloc is called as messages to nil are handled by the Objective-C runtime.

Is releasing memory of Objective-c 2.0 properties required?

Something I have been wondering about properties for a while. When you are using properties, do you need to override the release message to ensure the properties are released properties?
i.e. is the following (fictitious) example sufficient?
#interface MyList : NSObject {
NSString* operation;
NSString* link;
}
#property (retain) NSString* operation;
#property (retain) NSString* link;
#end
#implementation MyList
#synthesize operation,link;
#end
You should always release the backing variables in dealloc:
- (void) dealloc {
[operation release];
[link release];
[super dealloc];
}
Another way:
- (void) dealloc {
self.operation = nil;
self.link = nil;
[super dealloc];
}
That's not the preferred way of releasing the objects, but in case you're using synthesized backing variables, it's the only way to do it.
NOTE: to make it clear why this works, let's look at the synthesized implementation of the setter for link property, and what happens when it is set to nil:
- (void) setLink:(MyClass *) value {
[value retain]; // calls [nil retain], which does nothing
[link release]; // releases the backing variable (ivar)
link = value; // sets the backing variable (ivar) to nil
}
So the net effect is that it will release the ivar.
In non-GC applications, yes. It is usual to assign nil instead of releasing the ivars.
My best experience is to release ivars initialized with init and assign nil to properties with retain and copy mode.
In your case I would assign nil
- (void) dealloc {
self.operation = nil;
self.link = nil;
[super dealloc];
}
The best way to do this is:
- (void)dealloc {
[operation release], operation = nil;
[link release], link = nil;
[super dealloc];
}
It would indeed be more convenient to use the generated setter methods
self.operation = nil;
but that is frowned upon. You don't always know which thread an object is deallocated on. Thus using an accessor may cause problems by triggering KVO notifications.
The catch here is that you need to adapt your dealloc to match the object management policy defined in your #property. E.g. don't go releasing a iVar backing an (assign) property.
No, you override the -dealloc method. And yes, if you don't release your properties (or rather, the backing ivars), you will leak. So in your #implementation here you should have something like
- (void)dealloc {
[operation release];
[link release];
[super dealloc];
}
Synthesizing a property only creates getter and setter methods, and therefor won't release the ivar when the object is deallocated. You need to release the ivar yourself.
In pre-ARC whenever you see new, alloc, retain and copy, whether it is an instance var or a property you must release. In ARC whenever you have a strong variable you must set it to nil.
In either case you have to override dealloc().

Should I release this property?

I'm a objective c newbie, and i'm having a bit of problems with memory management, I've read the apple's memory management policies, however i need a bit of clarification here, this is pretty simple i guess, but i would like to ask you if I'm right:
Given this property:
#interface Test : NSObject {
NSArray *property1;
}
#property (nonatomic,retain) NSArray* property1;
#end
...
//And its implementation:
#implementation Test
#synthetize property1;
-(id) init {
if (self=[super init]) {
self.property1=[[[NSArray alloc] initWithCapacity:5] autorelease];
}
return self;
}
-(void) dealloc {
[super dealloc];
[property1 release];
}
#end
Is it right to issue an Autorelease message to the allocated object in the init method?, i do this cause in apple's document, says that every allocated object should be released by the developer, then, I think, alloc sets retain count to 1, then the property (nonatomic, retain) adds 1, so retain==2, then autorelease substracts 1, and when the dealloc method is called, property1 is released and retain count==0, am I right?
You have your memory management right, though Apple (and a lot of other people) generally recommend not using accessors in your initialization methods because accessors can have side effects beyond simply setting an instance variable that your class might not be set up to handle yet. And in that case, you wouldn't want to autorelease since you'd want ownership of the object.
one side note: in your dealloc, you need to release the property before calling [super dealloc], because [super dealloc] eventually deallocates the memory of the object, which includes the memory containing the property1 variable, so it is invalid to refer to that variable after you call [super dealloc]. It should be:
-(void) dealloc {
[property1 release];
[super dealloc];
}
One of the nice things about using properties is that you can encapsulate all of your "releasing" behavior regardless of whether your property is set to retain, copy, assign, or whatever by just doing this:
self.property1 = nil;
Personally I've gotten in the habit of setting all properties to nil (using self.property, not just accessing the member variable directly) in dealloc so that even if I change how the memory management works for the member variable it works correctly.

Should you set the delegate to nil in the class using the delegate or in the class itself

If class A is using class B and class A is class B's delegate, is it ok if the delegate is set to nil in class B's dealloc? I have seen code usually resetting the delegate to nil inside class A's dealloc but wasn't sure the real difference doing it one way or the other.
e.g. This is the usual way:
// somewhere in class A
- (void) someFunc {
self.b = [[B alloc] init];
self.b.delegate = self;
}
- (void) dealloc {
self.b.delegate = nil;
[self.b release];
}
Yes, you should set the classB's delegate property to nil in classA's dealloc.
It's not a memory management issue, because delegate properties should be marked assign, not retain, to avoid retain cycles (otherwise the dealloc will never be called). The issue is that otherwise classB might message classA after it has been released.
For example, if classB has a delagate call to say "being hidden", and classB is released just after classA, it would message the already dealloc'ed classA causing a crash.
And remember, you can't always guarentee the dealloc order, especial if they are autoreleased.
So yes, nil out the delegate property in classA's dealloc.
As far as I know, its best practice to (assign) a delegate, such that you avoid circular references on retain counts for situations just like this. If you've set up the property properly, ie:
#property (assign) id<BDelegate> delegate;
You shouldn't have to perform any memory management in the dealloc, as the retain count is not bumped when you call self.b.delegate = self; -- unlike using (retain) or (copy)
Make sense? It would be fine to set the delegate to nil, but whats the point?
First, a few observations...
You've forgotten to call [super dealloc] at the end of your own dealloc method.
Since 'a' created 'b', and if no other objects have retained 'b', there no point in nilling the delegate in the -dealloc, since 'b' is about to be destroyed anyhow. If it's possible that other objects have a reference to 'b' (meaning it might outlive 'a') then set the delegate to nil.
Object 'b' should be the one to take care of its delegate in its own -dealloc if necessary. (Generally, the delegator does not retain the delegate.)
Avoid using properties in -init... and -dealloc methods — Apple discourages this, and for good reason. (Not only could it have unexpected side effects, but can also cause nastier, crashier problems.)
Using properties (via the dot syntax) when you don't need to invisibly adds extra work. For instance, self.b.delegate = self is equivalent to [[self getB] setDelegate:self] — it's just syntactic sugar that makes it look like you're accessing the ivar directly, but you're actually not.
Using properties without understanding what they do can lead to trouble. If self.b retains the value (the property is set to "assign"), you have a memory leak on your hands.
Here's how I would probably write it:
- (void) someFunc {
b = [[B alloc] init];
b.delegate = self; // or [b setDelegate:self];
}
- (void) dealloc {
b.delegate = nil;
[b release];
[super dealloc];
}