Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How is release handled for @synthesized retain properties?

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?

like image 410
e.James Avatar asked Feb 03 '10 05:02

e.James


3 Answers

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.

like image 60
kennytm Avatar answered Nov 15 '22 22:11

kennytm


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.

like image 21
Dave Avatar answered Nov 15 '22 22:11

Dave


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.

like image 35
Dan Ray Avatar answered Nov 16 '22 00:11

Dan Ray