views:

408

answers:

1

Given the following definition of a class with retain properties:

@interface FeedEntry : NSObject<NSCoding>
{
    NSURL* url;
    NSData* source;
}

@property (retain) NSURL*   url;
@property (retain) NSData*  source;
@end

@implementation FeedEntry

@synthesize url;
@synthesize source;

-(void)encodeWithCoder:(NSCoder*)coder
{
    [coder encodeObject:url  forKey:@"url"];
    [coder encodeObject:source forKey:@"source"];
}

Why does the url property in initWithCoder method need the "retain":

-(id)initWithCoder:(NSCoder*)coder
{
    url = [[coder decodeObjectForKey:@"url"] retain];
    source = [coder decodeObjectForKey:@"source"];

    NSLog(@"got url=%@\n", url);
    return self;
}

Specifically, why doesn't the synthesized "get url" method retain the object? (I'm guessing the source property will need a retain as well).

+12  A: 

Quick answer:

When you set:

url = [[coder decodeObjectForKey:@"url"] retain];

you are not using the @property. You are manually setting the value of the instance variable url. You must, therefore, also manually retain the value.

To set the variable using the synthesized properties, you would instead call:

[self setUrl:[coder decodeObjectForKey:@"url"]];

or

self.url = [coder decodeObjectForKey:@"url"];

Either of these forms would make use of the synthesized methods, and handle the retain automatically.

Details:

In Objective-C, the @property and @synthesize keywords automatically create the getter and setter methods for you:

@interface MyClass
{
    id someValue;
}
@property (retain) id someValue;
@end

@implementation MyClass
@synthesize someValue;
@end

Is equivalent to:

@interface MyClass
{
    id someValue;
}
- (id)someValue;
- (void)setSomeValue:(id)newValue;
@end

@implementation MyClass
- (id)someValue { return someValue; }
- (void)setSomeValue:(id)newValue
{
    [newValue retain];
    [someValue release];
    someValue = newValue;
}
@end

This creates an important distinction between the "internal" member variable and the property having the same name. If you reference the member variable by name, you are bypassing the synthesized property methods.

e.James
Indeed! I have a feeling this is going to bite me fairly often. Thanks for your reply.
Justicle
My pleasure. It takes some getting used to, at first. Once you've been doing it for a while, it becomes second nature :)
e.James
That's one of the reasons I like to using a _ prefix on private instance variables, it makes it very easy to tell when you're using an instance variable vs anything else.
Marc Charbonneau
Thats a good point Marc.Coming from a C++ background, this feature seems to incredibly error prone without some kind of naming convention.afaik (maybe someone can correct me) the other "C-like" languages don't distinguish between "this.member_variable_or_property = some_value" and "member_variable_or_property = some_value".
Justicle
Just to add to Marc's comment: the @property declaration should remain *without* the _ prefix, and the @synthesize statement should look like this: @synthesize memberVariable = _memberVariable; This way the compiler will flag an error if you use memberVariable without self.
David Jacobson
Note: I have changed the memory management code for the retain property so that it matches the commonly-accepted implementation. This version prevents memory errors which can occur if `someValue` and `newValue` point to the same object.
e.James
Its nice to see others fixing old answers :) Note that you could also simply do nothing if `newValue == someValue`.
Georg Fritzsche
As long as we're on the subject, Apple seems to have changed the iVar conventions. The template files included in the latest version of Xcode all put the underscore at the end of the iVar. `fetchedResultsController_`
kubi
@Georg Fritzsche: I usually try to tidy up my old answers when I run across them. Searching for answers and finding your own is a very strange feeling. `:)`
e.James
@Georg Fritzsche: For some reason, I feel less comfortable with adding `if (newValue == someValue) { return; }` to the top of the property accessor. I can't really explain why, but it just has a whiff of trouble about it.
e.James
Hm, the same for `if (newValue != someValue) { /* do the stuff */ }`? This is what Apple has as an example too. :)
Georg Fritzsche