MLQ
MLQ

Reputation: 13511

Xcode requiring me to redeclare properties as instance variables

I have an object called SCPFAd and it is declared in its header file as follows:

@interface SCPFAd : NSObject

@property (strong, nonatomic) NSArray *imageURLs;
@property (strong, nonatomic) NSString *title;
@property (strong, nonatomic) NSString *price;
@property (strong, nonatomic) NSString *longDescription;
@property (strong, nonatomic) SCPFLocation *location;
@property (strong, nonatomic) SCPFCategory *category;
@property (strong, nonatomic) NSArray *properties;

@property (readonly, strong, nonatomic) NSString *sellerID;
@property (readonly, strong, nonatomic) NSString *timePosted;

- (id)initWithRawData:(NSDictionary *)rawData;
- (BOOL)displaysPrice;

@end

In the implementation file, I have an SCPFAd extension declared this way:

@interface SCPFAd ()
{
    NSMutableDictionary *_rawData;
    NSMutableArray *_imageURLs;
    NSString *_title;
    NSString *_price;
    NSString *_longDescription;
    SCPFLocation *_location;
    SCPFCategory *_category;
    NSMutableArray *_properties;
}

@property (strong, nonatomic) NSDictionary *rawData;

@property (strong, nonatomic) NSString *sellerID;
@property (strong, nonatomic) NSString *timePosted;
@property (strong, nonatomic) NSString *adID;

@end

I deliberately redeclared the properties rawData, imageURLs, and properties as instance variables because I want external objects to access or assign them as immutable types, but I'll be changing them internally.

What I don't understand is why, when I override the setters, I get a compiler error that says it can't find the variables _title, _price, _longDescription, _location, and _category. The error goes away when I redeclare title, price, longDescription, location, and category as above, but I see it as unnecessary--nothing in the class extension changes their external declarations.

This is how I'm overriding setTitle, for example:

- (void)setTitle:(NSString *)title
{
    _title = title;
    _rawData[@"name"] = title;
}

- (NSString *)title
{
    if (!_title) {
        _title = _rawData[@"name"];
    }
    return _title;
}

If I comment out NSString *_title; in the extension, the compiler says it can't find _title in the first line of the setter, and wherever it occurs in the getter. The getter used to work just fine, though, even without the redeclaration.

Upvotes: 0

Views: 323

Answers (4)

CRD
CRD

Reputation: 53000

If you implement a getter and a setter for a read-write property, or a getter for a read-only property then Clang (Xcode) will not synthesise the backing instance variable - see Apple's Encapuslating Data, note in the section You Can Implement Custom Accessor Methods.

You are implementing both the setter and the getter so you must provide your own instance variable if needed.

Upvotes: 0

DaiMaoTsai
DaiMaoTsai

Reputation: 304

When you override the setter and getter (not just the getter), Xcode assumes you want complete control and doesn't create the backing store (the _title). You have to do it yourself with

@synthesize title = _title

Upvotes: 0

Chris Devereux
Chris Devereux

Reputation: 5473

If you implement a getter, the compiler doesn't automatically create an ivar.

This is for a good reason. The property may (and, in my experience, usually is) created on request and returned, so in that case no instance variable is needed to store it and it would add a significant memory overhead to classes with a large number of such properties if every getter had an associated ivar.

One other comment. This:

NSMutableDictionary *_rawData;
// ...
@property (strong, nonatomic) NSDictionary *rawData;

May cause you problems. If rawData is set with an immutable dictionary, it will raise an exception when you attempt to mutate it later. Make sure you copy it on assign using -mutableCopy. (I assume you aren't copying it because it's marked strong, not copy. If you are, it's fine)

Upvotes: 1

Gavin
Gavin

Reputation: 8200

If you declare a property and then override both the getter and setter, it won't auto-synthesize the property. But you can just add a line to synthesize it to your implementation:

@synthesize title = _title;

As for having a property be an immutable type, and its backing instance variable be mutable, you're going to have an issue when from outside your class the immutable type is assigned to it, and you treat it as the mutable version, because it won't respond to the methods to mutate it. For example, you assign an NSArray to a variable, then try to treat it as an NSMutableArray, it won't work.

Upvotes: 2

Related Questions