Jeremy
Jeremy

Reputation: 268

Memory management and properties (init/dealloc)

Until yesterday I thought I understood how properties memory management works, but then I ran an "Analize" task with XCode and got plenty of "This object is not own here". Here is a simple example that describes my problem :

MyObservingObject.h:

@interface MyObservingObject : NSObject
@property(nonatomic, retain) NSMutableDictionary *observedDictionary;
-(id)initWithDictCapacity:(int)capacity;
@end

MyObservingObject.m:

@synthesize observedDictionary;

-(id)initWithDictCapacity:(int)capacity {
    self = [super init];
    if (self) {
        self.observedDictionary = [[[NSMutableDictionary alloc] initWithCapacity:capacity] autorelease];
    }
    return self;
}

- (void)dealloc {
    // The following line makes the Analize action say :
    // "Incorrect decrement of the reference count of an object that is not owned at this point by the caller"
    [self.observedDictionary release], self.observedDictionary=nil;

    [super dealloc];
}

What I don't understand is Why should I leave this property without calling release on it? My @property is set as retain (copy does the same), so when I'm doing self.myRetainProperty = X, then X got its retain count increased (it's owned by self), didn't it ?

Upvotes: 0

Views: 126

Answers (4)

Paul.s
Paul.s

Reputation: 38728

The reason for the compiler warning is because of the way you are retrieving the object.

By calling

[self.observedDictionary release];

you are in fact going through the accessor method defined as

- (NSDictionary *)observedDictionary;

This returns your object but due to the naming of observedDictionary the compiler assumes that there is no transfer of ownership e.g. the callee will not have to release this object unless they take a further retain. It is because of this that the compiler thinks you are going to do an overrelease by releasing an object that you don't actually own.

More specifically the convention for method names that transfer ownership is for them to start with copy, mutableCopy, alloc or new.

Some examples

Here I have used a name that does not imply transfer for ownership so I get a warning

- (id)object;
{
  return [[NSObject alloc] init];
}
//=> Object leaked: allocated object is returned from a method whose name ('object') does not start with 'copy', 'mutableCopy', 'alloc' or 'new'.  This violates the naming convention rules given in the Memory Management Guide for Cocoa

Fix 1: (don't transfer ownership)

- (id)object;
{
  return [[[NSObject alloc] init] autorelease];
}

Fix 2: (make the name more appropriate)

- (id)newObject;
{
  return [[NSObject alloc] init];
}

With this knowledge we can of naming convention we can see that the below is wrong because we do not own the returned object

[self.object release]; //=> Produced warnings

And to show a final example - releasing an object that implies ownership transfer with it's name

[self.newObject release]; //=> No Warning

Upvotes: 0

user529758
user529758

Reputation:

It did get increased, but when you set it to nil, the setter method first releases the backing instance variable, and only then does it retain and assign the new value. Thus setting the property to nil is enough, setting the ivar to nil leaks memory, though.

For your better understanding: the typical implementation of an autogenerated retaining setter is equivalent to something like

- (void)setFoo:(id)foo
{
    if (_foo != foo) {
        [_foo release];
        _foo = [foo retain];
    }
}

Also note that, as a consequence, you should never release properties like this. If you do so, the backing ivar may be deallocated, and messaging it (release by the accessor when setting the property to nil afterwards) can crash.

Upvotes: 1

trojanfoe
trojanfoe

Reputation: 122458

You should let the setter do the releasing for you, so remove the call to release in dealloc:

- (void)dealloc {
    self.observedDictionary=nil;

    [super dealloc];
}

This is because the setter will be synthensized to something like:

- (void)setObject:(id)object
{
    [object retain];
    [_object release];
    _object = object;
}

Which will work as desired when you pass in nil.

Upvotes: 2

Rubycon
Rubycon

Reputation: 18346

You don't need to do

[self.observedDictionary release]

before

self.observedDictionary=nil;

This is enough, because this is a property, and it will automatically send release to previous value

self.observedDictionary=nil;

Upvotes: 1

Related Questions