user944943
user944943

Reputation: 113

retain/release issues

I just analyzed my iPhone project, and was very confused by the result XCode(4) gave me. For example, in one of my view controllers I have this code:

@property (nonatomic, retain) NSArray* menuItems;
@property (nonatomic, retain) NSArray* menuItemsOptions;

- (void)viewDidLoad 
{
   [super viewDidLoad];

   self.menuItems = [[NSArray alloc] initWithObjects:
                    NSLocalizedString(@"Foo", nil), 
                    NSLocalizedString(@"Bar", nil), 
                    nil];

  [self.menuItems release];

  self.menuItemsOptions = [[NSArray alloc] initWithObjects:
                           NSLocalizedString(@"More foo", nil), 
                           NSLocalizedString(@"more bar", nil), 
                           nil];

  [self.menuItemsOptions release];
...
}

menuItems as well as menuItemsOptionsare properties with the retainoption. If I press analyze, XCode will show an error for the line [self.menuItems release];:

http://i54.tinypic.com/2rqkfaf.png

To confuse me even more, XCode will not show errors for the line [self.menuItemsOptions release];

Similar situation in another method:

http://i55.tinypic.com/10hof9c.png

theSelectedBegin and theSelectedEnd are again properties with retain option.

The reason why I'm posting this is that my app will actually crash with a very cryptic/not understandable backtrace within a third party library unless I add the copy seen on the last picture but dont add the release. Adding the releaseor omitting the copy will make the app crash again, this is why i decided to run the analyzer.

What am I doing wrong?

Upvotes: 0

Views: 1353

Answers (3)

BJ Homer
BJ Homer

Reputation: 49034

The reason you're getting a warning from the analyzer is that a getter method is not required to actually return the exact same object as what you passed in to the setter. For example, imagine the following code:

- (void)doSomethingWithAString:(NSString *)aString {
    self.myName = [[NSString alloc] initWithFormat:@"%@ the Great", aString];
    [self.myName release];
}

The string is created with an owning method (-init...), so you own it. Then you gave it to the myName property, which took ownership. Now you need to release the ownership you received from the -init... method, which is done by calling -release. Great.

The problem with the above code is that [self.myName release] might not release the same object you passed in to the setter. Imagine if the setter were implemented like this:

- (void)setMyName:(NSString *)someString {
    // Make sure to trim whitespace from my name!
    NSCharacterSet *whitespaceSet = [NSCharacterSet whitespaceCharacterSet];
    NSString *strippedString = [someString stringByTrimmingCharactersInSet:whitespaceCharacterSet];

    [myName autorelease];
    myName = [strippedString retain];
}

Note that the object you passed into the setter is not the object that was stored to the backing ivar. When you call [self.myName release], you're releasing the stripped string, not your original string. The original string was now leaked, and the stripped string has been over-released.

In short, never assume that a getter returns the same object you passed to the setter.

Upvotes: 1

Sam
Sam

Reputation: 27354

Try changing someMethod to:

-(void) someMethod:(NSDate*)fromDate toDate:(NSDate*)toDate
{
   if (editBegin)
   {
      NSDate *copiedDate = [fromDate copy];
      self.theSelectedBegin = copiedDate;
      [copiedDate release];
   }
   else
   {
      NSDate *copiedDate = [fromDate copy];
      self.theSelectedEnd = copiedDate;
      [copiedDate release];
   }
}

If you are using copy for the properties theSelectedBegin and theSelectedEnd (which I recommend), like:

@property (nonatomic, copy) NSDate *theSelectedBegin;
@property (nonatomic, copy) NSDate *theSelectedEnd;

The following code is equivalent to the above, but more concise and clean.

-(void) someMethod:(NSDate*)fromDate toDate:(NSDate*)toDate
{
   if (editBegin)
   {
      self.theSelectedBegin = fromDate;
   }
   else
   {
      self.theSelectedEnd = fromDate;
   }
}

When you do[myObj copy] a new object is returned. Doing [myObj retain] returns the SAME object with an increased retain count. So effectively, the following is BAD code:

@property (nonatomic, copy) NSDate *myDate;
[...]

self.myDate = [someDate copy];
[self.myDate release];

Breaking it down looks more like...

@property (nonatomic, copy) NSDate *myDate;
[...]

NSDate *copyDate = [someDate copy];  // never gets released
self.myDate = copyDate;             // good so far for self.myDate
[self.myDate release];              // just released self.myDate (note: copyDate not released)

Upvotes: 1

Caleb
Caleb

Reputation: 124997

One of the appealing features of properties is that the property accessors take care of retaining and releasing the objects they point to. I can't think of a case where one would explicitly retain or release a property.

Upvotes: 0

Related Questions