Objective-C:在重写超类getter并试图访问ivar时发生编译错误

时间:2023-01-15 18:29:11

I'm working on building an iOS 6 app.

我正在开发一个iOS 6应用。

I have a class TDBeam which inherits from superclass TDWeapon.

我有一个类TDBeam继承自超类tdweapons。

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

超类tdweapons在tdweapons中声明@属性。h文件:

@interface TDWeapon : UIView

@property (nonatomic) int damage;

@end

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

我没有显式@synthesize属性,因为我让Xcode自动这么做。

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

在TDBeam子类中,我重写TDBeam中的getter。m文件:

#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:

Xcode按预期自动完成getter方法名。但是当我尝试引用_damage实例变量(从超类继承)时,我得到了一个编译器错误:

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?

我在这里做错了什么?我尝试过显式地添加@synthesize,并更改_damage ivar的名称,但是编译器并没有“看到”它或来自父类的任何其他ivars。我以为ivars在子类中是可见和可访问的?

2 个解决方案

#1


20  

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.

合成的ivars对子类来说是不可见的,无论它们是显式的还是自动创建的:@合成实例变量的可见性是什么?由于它们是在实现文件中有效声明的,所以它们的声明没有包含在包含子类的“翻译单元”中。

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.

如果您真的想要直接访问ivar,您必须显式地声明它(以其默认的“受保护”形式)在子类可以看到的某个地方,例如在私有头中的超类的类扩展。

#2


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.

关于Stack Overflow这个主题有很多帖子,没有一个提供简单的具体建议,但是这个主题最简洁地总结了,Josh的回答是最好的。

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.

他没有直截了当地说,如果这是你想做的事情,就不要使用@property。如他所说,在基类中声明常规的受保护变量,并在需要时编写自己的setter和getter。任何子类都可以看到ivar,这些子类可以编写自己的setter /getter。

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.

创建私有头来托管匿名类别并在子类中重新设置ivars的想法在很多层面上似乎都是错误的。我也肯定我可能漏掉了一些基本的点。


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.

在经历了一些失眠之后,受到斯坦福大学2013年iTunes U课程的启发,我认为这是解决这个问题的一个例子。

MYFoo.h

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

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.

然后,您只需用所需的自定义逻辑替换子类中的makevalueformynewproperty。您的属性在基类中是“受保护的”,但是您可以控制如何创建它,这基本上是您在大多数情况下试图实现的。

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).

如果您的makevalueformynewproperty方法需要访问基类的其他ivars,那么它们至少必须是公共readonly属性(或者只是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.

这并不是说“过度的清除一个getter”,但是它实现了同样的事情,有一点想法。我很抱歉,如果在尝试使这个例子通用,一些优雅和清晰已经丢失。

#1


20  

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.

合成的ivars对子类来说是不可见的,无论它们是显式的还是自动创建的:@合成实例变量的可见性是什么?由于它们是在实现文件中有效声明的,所以它们的声明没有包含在包含子类的“翻译单元”中。

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.

如果您真的想要直接访问ivar,您必须显式地声明它(以其默认的“受保护”形式)在子类可以看到的某个地方,例如在私有头中的超类的类扩展。

#2


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.

关于Stack Overflow这个主题有很多帖子,没有一个提供简单的具体建议,但是这个主题最简洁地总结了,Josh的回答是最好的。

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.

他没有直截了当地说,如果这是你想做的事情,就不要使用@property。如他所说,在基类中声明常规的受保护变量,并在需要时编写自己的setter和getter。任何子类都可以看到ivar,这些子类可以编写自己的setter /getter。

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.

创建私有头来托管匿名类别并在子类中重新设置ivars的想法在很多层面上似乎都是错误的。我也肯定我可能漏掉了一些基本的点。


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.

在经历了一些失眠之后,受到斯坦福大学2013年iTunes U课程的启发,我认为这是解决这个问题的一个例子。

MYFoo.h

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

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.

然后,您只需用所需的自定义逻辑替换子类中的makevalueformynewproperty。您的属性在基类中是“受保护的”,但是您可以控制如何创建它,这基本上是您在大多数情况下试图实现的。

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).

如果您的makevalueformynewproperty方法需要访问基类的其他ivars,那么它们至少必须是公共readonly属性(或者只是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.

这并不是说“过度的清除一个getter”,但是它实现了同样的事情,有一点想法。我很抱歉,如果在尝试使这个例子通用,一些优雅和清晰已经丢失。