Objective-C中具有合成只读属性的类的子类无法访问实例变量。

26
在超类 MyClass 中:
@interface MyClass : NSObject

@property (nonatomic, strong, readonly) NSString *pString;

@end

@implementation MyClass

@synthesize pString = _pString;

@end

在子类MySubclass

@interface MySubclass : MyClass

@end

@implementation MySubclass

- (id)init {
    if (self = [super init]) {
        _pString = @"Some string";
    }
    return self;
}
问题在于编译器不认为_pStringMySubclass的成员,但我在MyClass中访问它没有问题。
我错过了什么?

尝试使用 [super setPString:] 或者 [self setPString:]。 - TeaCupApp
1
尝试像下面这样移除只读属性:@property(nonatomic,strong)NSString *pString; - Dinesh
3个回答

54

@synthesize 生成的实例变量 _pString私有的,只能在 MyClass 中访问。你需要将其修改为受保护的,这样 MySubclass 才能访问它。

MyClass@protected 部分中添加一个 ivar 声明,如下所示:

@interface MyClass : NSObject {
    @protected
    NSString *_pString;
}

@property (nonatomic, strong, readonly) NSString *pString;

@end

现在像平常一样合成访问器,你的变量就可以被你的子类访问了。


是的,如果它来自具有访问底层变量权限的子类,则会这样做。 - borrrden
2
@Owl 你将无法通过属性来设置字符串,但是你可以将字符串设置到后备变量中,这将导致 pString 属性的值发生改变。你需要设置 _pString = @"abc",而不是 self.pString = @"abc" - Sergey Kalinichenko
@Owl,就像dasblinken所说的那样!你不必通过其属性接口设置变量。这样,不继承该类的对象无法更改变量。它只对子类是只读的。 - borrrden
关于堆栈的语义问题...“oldest”答案是按创建时间还是编辑时间排序? - borrrden
1
好的回答。我个人会使用双下划线来表示您正在访问超类上的受保护的ivar,并明确地使用@synthesize。 - SmileBot
1
小提醒:在子类中使用实例变量(_myVariable)而不是访问器(self.myVariable)。 - Rudolf Real

5

我熟悉这个问题。您在.m类中合成变量,因此它不会随头文件一起导入,因为_pString变量将作为实现的一部分而不是接口的一部分创建。解决方法是在您的头文件接口中声明_pString,然后无论如何都要进行合成(它将使用现有变量而不是创建私有变量)。

@interface MyClass : NSObject
{
    NSString *_pString; //Don't worry, it will not be public
}

@property (nonatomic, strong, readonly) NSString *pString;

@end

0

给出的答案完全有效。这是另一种答案,显然苹果公司更喜欢一些

您可以定义一个类的私有扩展,一个MyClass+Protected.h文件,需要包含在MyClass.mMySubclass.m中。

然后,在这个新文件中,将属性重新定义为readwrite

@interface MyClass ()
@property (strong, readwrite) NSString * pString;
@end

这种替代方案允许您使用访问器self.pString而不是实例变量_pString

注意:您仍然需要保留MyClass.hpString的定义。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接