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().
Related
This is something I should have cleared up long ago, but I just need to know the best practice for deallocating in the following scenario.
In my header file I declare a reference to an IBOutlet as follows:
#interface Test : UIViewController {
UIButton *_loginBtn;
}
#property (nonatomic, retain) IBOutlet UIButton *loginBtn;
And in the implementation file I associate the instance variable to the property and deallocate as follows:
#implementation Test
#synthesize loginBtn = _loginBtn;
...
- (void) dealloc {
[_loginBtn release];
self.loginBtn = nil;
[super dealloc];
}
- (void) viewDidUnLoad {
[_loginBtn release];
self.loginBtn = nil;
[super viewDidUnLoad];
}
Am I correct in the deallocating the instance variable and setting the property to nil and doing this in both the viewDidUnLoad and dealloc methods?
There is no need for self.loginBtn = nil; in dealloc, the previous line released it. It is best not to use the property to release it in dealloc. The reason for releasing vs setting the property to nil is that the setter is a method call and the class is in the midst of tearing down and things may be unstable.
In viewDidUnLoad release any properties that are IBOutlets with self.theOutlet = nil;, in this case the _loginBtn release]; is not needed and redundant. Also release any other objects that you can easily re-create.
If properties are used they should be used for all accesses in the class with two exceptions: init and dealloc. In both of these cases the class is partially complete. In these two cases it is best to use the ivar directly in init (if necessary) and release in dealloc.
No, this is incorrect. By first releasing _loginBtn and then setting the property to nil, you release the instance twice. The correct way to do it is to release _loginBtn and then set _loginBtn to nil.
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];
}
When you synthesize a property (see below)
#interface CelestialBody : NSObject {
NSString *name;
}
...
#interface Planet : NSObject {
NSString *name;
int mass;
CelestialBody *moon;
}
#property(nonatomic, retain) NSString *name;
#property(assign) int *mass;
#property(nonatomic, retain) CelestialBody *moon;
...
#implementation Planet
#synthesize name;
#synthesize mass;
#synthesize moon;
...
You get setters and getters for each of the iVars (i.e.)
[newPlanet setName:#"Jupiter"];
[newPlanet setMass:57];
NSString *closestName = [newPlanet name];
int largestMass = [newPlanet mass];
CelestialBody *newMoon = [[CelestialBody alloc] initWithName:#"Callisto"];
[self setMoon:newMoon];
[newMoon release];
but you also get the ability to release the object using ...
// Releases the object (frees memory) and sets the object pointer to nil.
[self setMoon: nil];
There will of course be deallocs for each Class.
// Moon
-(void)dealloc {
[name release];
[super dealloc];
}
// Planet
-(void)dealloc {
[name release];
[moon release];
[super dealloc];
}
Am I getting this right?
gary
Unless your planet object is declared as a property within some other class, using the retain/copy attributes, you can't release it this way.
When you declare a property using retain/copy, the resulting setter will release the old value and assign the new value, retaining or copying it in the process. If you pass nil, you will release the old value and assign nil, retaining or copying it, and retaining/copying nil is nil, so effectively you end up releasing the old value and assigning nil to the ivar.
This is an acceptable way to release instance variables.
In order to be able to release your newPlanet instance this way, you'd have to have declared it in a class as a property with either retain or copy.
As a further example, since your planet object declares its properties in this way, you could release those using this method.
Or in the Planet class's dealloc method, you could do:
self.name = nil;
This would release name and assign nil to it.
"you also get the ability to release the object"
Yes, as long as you didn't declare it with the assign attribute.
As you probably know, one of the reasons (although perhaps not the primary one) for using declared properties is that you can do:
self.moon = aMoon;
rather than;
[self setMoon:aMoon];
They are equivalent. That means that your deallocation can look like this:
self.moon = nil; // Releases and sets to nil
But remember to never just do:
moon = nil; // Sets to nil, doesn't release
It's very good practice to not only release the object, but to set the variable to nil, as you do, because otherwise some other code could mistakenly try to use the pointer that is left in the variable.
Your example shows the synthesis of one class's ivars (those of Planet) but the use of another (whatever "self" is). Is the "newPlanet" property of "self" in your last example also synthesized as (retain)? If so, then: Yes, setting newPlanet to nil will release whatever self's old "newPlanet" was.
I think you are not getting it right.
After your question update, yes, you can do that, and also:
self.moon = [[CelestialBody alloc] initWithName:#"Callisto"];
and release it later, probably in your dealloc method:
self.moon = nil;
Apple Objective-c 2.0 Properties and Memory Management docs are pretty good. Check Mac Dev Center library.
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.
I have some questions about synthesized properties in Objective-C. The full list follows, but the basic question is this: How does the compiler ensure that the ivars for synthesized properties are properly released, even though my code may or may not include release methods in dealloc?
Note: I decided not to post these as individual questions because they are so closely related and because there are a handful of existing questions that touch on the individual issues without really getting to the heart of the matter.
Somewhat similar questions:
Does property retain need a release?
What's the difference between property and synthesize?
Question on retain attribute with property and synthesize
Setup: Consider a class with a single property:
#interface Person : NSObject
{
NSString * name;
}
#property (nonatomic, retain) name;
#end
Question #1: The very basic case:
#implementation Person
#synthesize name;
#end
With this setup, I assume that name will be automatically released whenever a Person object is released. In my mind, the compiler simply inserts [name release] into the dealloc method as if I had typed it myself. Is that correct?
Question #2: If I choose to write my own dealloc method for this class, and I omit a call to [name release], will that leak?
#implementation Person
#synthesize name;
- (void)dealloc { [super dealloc]; }
#end
Question #3: If I choose to write my own dealloc method for this class, and I include a call to [name release], will that result in a double-release, since #synthesize has already taken care of it for me?
#implementation Person
#synthesize name;
- (void)dealloc { [name release]; [super dealloc]; }
#end
Question #4: If I choose to write my own property accessor for this class, but I do not write my own dealloc method, will name be leaked?
#implementation Person
#dynamic name;
- (void)setName:(NSString *)newName
{
[newName retain];
[name release];
name = newName;
}
#end
Question #5: I have a feeling (based on experience) that none of the above scenarios will result in leaks or double-releases, since the language has been designed to avoid them. That, of course, raises the question of "how?". Is the compiler simply smart enough to keep track of every possible case? What if I were to do the following (note that this is a ludicrous example, just meant to illustrate my point):
void Cleanup(id object) { [object release]; }
#implementation Person
#synthesize name;
- (void)dealloc { Cleanup(name); }
#end
Would that fool the compiler into adding another [name release] to the dealloc method?
Q1:
No. #synthesize does not modify the -dealloc for you. You have to -release the name yourself.
Q2:
Yes it will leak. Same reason as Q1.
Q3:
No it won't double-release. Same reason as Q1.
Q4:
Yes it will leak. Same reason as Q1.
Q5:
No it won't double-release. Same reason as Q1.
You can check this yourself by overriding -retain and -release and -dealloc to report what is going on.
#import <Foundation/Foundation.h>
#interface X : NSObject {}
#end
#implementation X
-(oneway void)release {
NSLog(#"Releasing %p, next count = %d", self, [self retainCount]-1);
[super release];
}
-(id)retain {
NSLog(#"Retaining %p, next count = %d", self, [self retainCount]+1);
return [super retain];
}
-(void)dealloc {
NSLog(#"Dealloc %p", self);
[super dealloc];
}
#end
#interface Y : NSObject {
X* x;
}
#property (nonatomic, retain) X* x;
#end
#implementation Y
#synthesize x;
- (void)dealloc { [x release]; [super dealloc]; }
#end
int main () {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
Y* y = [[Y alloc] init];
X* x = [[X alloc] init];
y.x = x;
[y release];
[x release];
[pool drain];
return 0;
}
In Q1, Q2 and Q4, the last -retainCount of x is 1, so there is a leak, and in Q3 and Q5 the last -retainCount is 0 and -dealloc is called, so there is no leak.
From the Objective-C documentation on properties:
dealloc
Declared properties fundamentally take
the place of accessor method
declarations; when you synthesize a
property, the compiler only creates
any absent accessor methods. There is
no direct interaction with the dealloc
method—properties are not
automatically released for you.
Declared properties do, however,
provide a useful way to cross-check
the implementation of your dealloc
method: you can look for all the
property declarations in your header
file and make sure that object
properties not marked assign are
released, and those marked assign are
not released.
This essentially answers all your questions.
The simple and general rule: if you allocate, retain, or copy an object, YOU have to release it.
When you use the retain setter semantic setting in a #synthesize statement, you're asking the compiler to build for you a setter that calls retain on the object. Nothing more, nothing less. And since you are retaining that object (even though it's via magically auto-generated code), you have to release it, and where to release it is in -(void)dealloc.
Something else it's worth knowing - if you have a synthesised property, setting that property to nil (using dot syntax, of course) will release the ivar for you.