【问题标题】:Why does the compiler warn when redeclaring base class's readwrite property as readonly in subclass?为什么在子类中将基类的读写属性重新声明为只读时编译器会发出警告?
【发布时间】:2011-10-31 19:49:57
【问题描述】:

稍后引用的 Apple 文档似乎表明这是允许的,尽管我承认直到现在我才没有理由在子类中这样做。

我有一个带有公共读写属性的基类和一个我将属性重新声明为只读的子类。子类还有一个类扩展,它再次将属性重新声明为读写,以实现常见的“公共只读,私有读写”Objective-C 模式。但是,我收到以下编译器警告:

warning: Semantic Issue: Attribute 'readonly' of property 'foo' restricts attribute 'readwrite' of property inherited from 'Base'

我在 10.7 上使用 Xcode 4.1 build 4B110 和 LLVM 2.1(尽管 LLVM GCC4.2 和 GCC4.2 给出相同的警告)。

这是一个显示编译器警告的精简示例:

#import <Foundation/Foundation.h>

@interface Base : NSObject
@property (nonatomic, readwrite) BOOL foo;
@end

@implementation Base
@dynamic foo;
@end

// Subclass
@interface Sub : Base
@property (nonatomic, readonly) BOOL foo;
@end

// Class extension 
@interface Sub ()
@property (nonatomic, readwrite) BOOL foo;
@end

@implementation Sub
@dynamic foo;  // it warns with @synthesize as well
@end

这是来自 Apple 的 The Objective-C Programming Language 的相关段落:

财产再申报

你可以在子类中重新声明一个属性,但是(除了 readonly 与 readwrite )你必须在整个重复它的属性 子类。对于在 a 中声明的属性也是如此 类别或协议——而属性可以在类别中重新声明 或协议,属性的属性必须全部重复。

如果你在一个类中声明一个属性为只读,你可以重新声明它 作为类扩展(参见“扩展”)、协议中的读写,或 在子类中(请参阅“使用属性进行子类化”)。在班级的情况下 扩展重新声明,该属性之前重新声明的事实 任何@synthesize 语句都会导致setter 被合成。这 将只读属性重新声明为读/写的能力使两个 常见的实现模式:不可变类的可变子类 (NSString、NSArray 和 NSDictionary 都是示例)和一个属性 有一个只读的公共 API,但有一个私有的读写实现 类的内部。以下示例显示使用类扩展 提供在公共标头中声明为只读的属性 但被私下重新声明为读/写。

我一直在类扩展中重新声明公共只读属性 readwrite,但我想我从来没有理由将它作为一个子类。但是,除非我读错了,否则上面的段落似乎表明它是洁净的。谁能让我直截了当和/或调和文档和编译器之间的明显冲突?

我为什么要这样做?当然,我的实际情况要复杂得多。如果需要,我可以进行设计更改以解决此问题,但这似乎是摩擦最小的替代方案(这样做的需要是由其他更改驱动的)。

【问题讨论】:

    标签: objective-c properties


    【解决方案1】:

    它说您可以将readonly 属性重新声明为readwrite,但您正在做相反的事情。你不能/不应该这样做,因为这样做是可能的:

    Sub* s = [[[Sub alloc] init] autorelease];
    Base* b = s; 
    b.foo = YES; //legal for `Base` objects, but not legal for `Sub` objects
    

    这违反了the Liskov Substitution Priciple

    【讨论】:

    • 汤姆说的话。当超类公开提供读写时,将子类设为只读是没有意义的。通常,您在类层次结构中添加可变性(请参阅操作系统中提供的各种不可变 -> 可变类集群示例)。
    • 感谢汤姆和 bbum。我(应该)知道得更好。我想我这个周末花了太长时间思考我的实际问题,而忽略了第一原则。我什至有第二次机会抓住自己,但我的大脑通过多次阅读以某种方式过滤掉了文档第二段中的“或在一个子类中”。晚餐休息后回到你的答案,我觉得这个有点傻。感谢您的澄清,我喜欢 Liskov 链接!
    猜你喜欢
    • 2013-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多