Reputation: 113
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 menuItemsOptions
are properties with the retain
option. 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 release
or 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
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
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
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