【问题标题】:Accessing Properties When Subclassing [duplicate]子类化时访问属性[重复]
【发布时间】:2014-05-02 22:13:39
【问题描述】:

我最近意识到使用 self.whatever = thing 从 init 内部访问类的属性是不合适的形式,而应该直接使用 _whatever = thing 访问属性。当我为基类设置值时,这很好用。但是,当我尝试在子类的 init 中设置值时,我收到错误“使用未声明的标识符 _whatever”。从子类的 init 内部使用 self.whatever 可以正常工作。

为了清楚起见,对于 @property int 在基础对象的接口中声明的任何内容,都会编译:

-(id) init {
    self = [super init];
    if (self) {
        [self createName];

        self.whatever = 100;
    }
    return self;
}

这不是:

-(id) init {
    self = [super init];
    if (self) {
        [self createName];

        _whatever = 100;
    }
    return self;
}

我想我在这里误解了一些东西。我尝试搜索我做错了什么,但我不知道要搜索的正确词。

根据要求,基类的标头:

#import <Foundation/Foundation.h>

@interface DTCharacter : NSObject

@property BOOL isIdle;

@end

我正在使用自动合成,所以在基类的实现中没有关于这个变量的任何内容。

子类的头文件也没有提及或引用变量。

我正在尝试在子类的实现中分配它:

-(id) init {
    self = [super init];

    if (self) {
        [self createName];

        _isIdle = YES;  //says use of undeclared identifier
        //this would work: self.isIdle = YES;
    }
return self;
}

【问题讨论】:

  • 实例变量默认是类私有的。为了让孩子们可以看到它们,你必须在你的类中声明它们@interface
  • 我在基类的@interface 中声明它们,而不是在实现中。
  • 在您的头文件中,还是在您的*.m 文件的@interface 部分中?你需要做前者。如果您正在这样做,请为显示错误的两个类发布一个简短但完整的头文件和实现文件。
  • 在我的标题中。我稍后会发布这些内容。

标签: objective-c inheritance initialization subclass


【解决方案1】:

访问initdealloc 中的属性可能会出现问题,如果该属性 访问器方法对对象的状态做出假设,这可能不是 在部分构造的对象中实现。

但这仅适用于类本身的属性,不适用于 超类。之后

self = [super init];

self 的“超级部分”已完全构造,因此可以安全调用

self.whatever = 100;

在子类的init方法中,设置在超类中声明的属性。

_whatever = 100;

在子类中不起作用,因为自动合成的实例变量 仅在超类本身中可见。您可以明确声明它以使其成为 对子类可见,但没有必要这样做。

【讨论】:

  • 谢谢你,为我彻底澄清了。一件事,然后在子类的 init 中使用 self.whatever 是否被认为是好的形式(或可接受的形式)?还是应该避免它,因为它从表面上看是违反 Objective-C 最佳实践的?
  • @bazola:据我了解,如果“whatever”是 superclass 的一个属性,那么使用 self.whatever 是完全可以接受和更可取的,因为超类的 init 方法已完成。调用来自子类的 init 方法并不重要。
  • 是的,我已经尝试在我的回答中的第一条评论中说同样的话。
  • 理想情况下它应该是self = [super initWithWhatever:100];,没有更多需要。
【解决方案2】:

Martin 的答案是最好的,因为您确实不需要为超类属性执行此操作(并且可以说不应该这样做,理论上类实现应该是私有的,即使来自子类,也应该尽可能地私有)。但是,如果您确实想这样做,这里有一个示例来说明如何操作:

main.m:

#import <Foundation/Foundation.h>
#import "PGRBase.h"

int main(int argc, const char * argv[])
{

    @autoreleasepool {
        PGRChild * theObject = [PGRChild new];
        NSLog(@"Property is %d\n", theObject.prop);

    }
    return 0;
}

PGRBase.h:

#import <Foundation/Foundation.h>

@interface PGRBase : NSObject {
    int _prop;
}

@property (readwrite, nonatomic, assign) int prop;

@end

@interface PGRChild : PGRBase

@end

PGRBase.m:

#import "PGRBase.h"

@implementation PGRBase

- (instancetype)init
{
    self = [super init];
    if ( self ) {
        _prop = 1;
    }
    return self;
}

@end

@implementation PGRChild

- (instancetype)init
{
    self = [super init];
    if ( self ) {
        _prop = 2;
    }
    return self;
}

@end

NSLog() 通话记录 Property is 2

【讨论】:

    【解决方案3】:

    有代码 sn-p 如何使子类可以访问 ivars:

    @interface SomeBase : NSObject
    {
    @protected
        WhateverType* _whatever;
    }
    @end
    

    所以_whatever 将对SomeBase 的子类可见。

    【讨论】:

    • 有道理,到目前为止,我一直让它自动合成我的属性。谢谢!
    • 实际上这是行不通的。它会让我在基类的实现中做你描述的事情,但它不会让我在子类中做。如果我尝试只做任何 = 100 而不是 self.whatever,它仍然无法识别变量。
    • @Cy-4AH:你的说法不正确。实例变量默认使用下划线前缀合成。
    • @MartinR,我不确定默认情况下来自哪个版本的 Xcode ivar 成为下划线前缀。 @synthesize whatever = _whatever; 适用于所有版本。
    猜你喜欢
    • 2011-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-04
    • 1970-01-01
    相关资源
    最近更新 更多