Objective-C: Compiler error when overriding a superclass getter and trying to access ivar
Asked Answered
E

2

15

I'm working on building an iOS 6 app.

I have a class TDBeam which inherits from superclass TDWeapon.

The superclass TDWeapon declares a @property in the TDWeapon.h file:

@interface TDWeapon : UIView

@property (nonatomic) int damage;

@end

I do not explicitly @synthesize the property, as I'm letting Xcode automatically do so.

In the subclass TDBeam I override the getter in the TDBeam.m file:

#import "TDBeam.h"

@implementation TDBeam

- (int)damage {
    return _damage;
}

@end

Xcode auto-completes the getter method name, as expected. But when I attempt to reference the _damage instance variable (inherited from the superclass), I get a compiler error:

Use of undeclared identifier '_damage'

What am I doing wrong here? I've tried explicitly adding @synthesize, and changing the name of the _damage ivar, but the compiler doesn't "see" it or any other ivars from the superclass. I thought ivars were visible and accessible from subclasses?

Ema answered 28/10, 2012 at 21:52 Comment(0)
T
21

Synthesized ivars are not visible to subclasses, whether they are explicitly or automatically created: What is the visibility of @synthesized instance variables? Since they are effectively declared in the implementation file, their declaration isn't included in the "translation unit" that includes the subclass.

If you really want to access that ivar directly, you'll have to explicitly declare it (in its default "protected" form) somewhere that the subclass can see it, such as a class extension of the superclass in a private header.

Thomajan answered 28/10, 2012 at 22:2 Comment(4)
Awesome, thanks for the clarification. I was starting to suspect this was the case. In this particular example, using [super damage] instead of _damage in the subclass should get me what I'm looking for.Ema
Actually @Ema shouldn't you actually call [self damage] so that you're accessing the damage of the subclass and not the superclass?Cerussite
@Cerussite Calling [self damage] (or using self.damage for that matter) inside the implementation is going to cause a stack overflow, as it's recursive :) Using [super damage] will call the auto-synthesized getter in the superclass, which effectively just returns the value of _damage.Ema
Sorry @smileyborg, I didn't even notice the context. You're right.Cerussite
V
2

There are a lot of posts on this topic on Stack Overflow, none of which offer simple concrete advice, but this topic sums it up most succinctly, and Josh's answer is the best in any.

What he kinda stops short of saying outright, is, if this is the kind of thing you want to do, don't use @property at all. Declare your regular protected variable in your base class as he says, and write you're own setters and getters if you need them. The ivar will be visible to any subclasses who can then write their own setters/getters.

At least that's where i've landed on the issue, although I'd a total newb to subclassing.

The idea of creating private headers to host your anonymous category and re-@sythesizing your ivars in your subclass just seems wrong on so many levels. I'm also sure I've probably missed some fundamental point somewhere.


Edit

Okay after some lost sleep, and inspired by Stanford's 2013 iTunes U course, here I believe is an example solution to this problem.

MYFoo.h

#import <Foundation/Foundation.h>

@interface MYFoo : NSObject
// Optional, depending on your class
@property (strong, nonatomic, readonly) NSString * myProperty;
- (NSString *)makeValueForNewMyProperty; //override this in your subclass
@end

MYFoo.m

#import "MYFoo.h"

@interface MYFoo ()
   @property (strong, nonatomic, readwrite) NSString * myProperty;
@end

@implementation MYFoo
// Base class getter, generic
- (NSDateComponents *)myProperty {
    if (!_myProperty) {
        _myProperty = [self makeValueForNewMyProperty];
    }
    return _myProperty;
}


// Replace this method in your subclass with your logic on how to create a new myProperty
- (NSString *)makeValueForNewMyProperty {
    // If this is an abstract base class, we'd return nil and/or throw an exception
    NSString * newMyProperty = [[NSString alloc]init];
    // Do stuff to make the property the way you need it...
    return newMyProperty;
}

@end

Then you just replace makeValueForNewMyProperty in your subclass with whatever custom logic you need. Your property is 'protected' in the base class but you have control over how it is created, which is basically what you are trying to achieve in most cases.

If your makeValueForNewMyProperty method requires access to other ivars of the base class, they will, at the very least, have to be be public readonly properties (or just naked ivars).

Not exactly 'over-ridding a getter' but it achieves the same sort of thing, with a little thought. My apologies if, in trying to make the example generic, some elegance and clarity has been lost.

Vane answered 7/2, 2013 at 9:44 Comment(1)
You're not entirely wrong, but I still think you should use a property declaration. It's ivar synthesis that causes the problem, not the property. Properties just declare methods, while synthesis has two parts: creation of the declared methods and of storage (an ivar). Either of those parts can be done manually according to your needs. Take a look at these two classes: gist.github.com/woolsweater/4734460Thomajan

© 2022 - 2024 — McMap. All rights reserved.