スーパークラスで読み書き可能なプロパティをサブクラスで読み取り専用にできますか?

投稿者: user5443

 以下のように、スーパークラスで readwrite 属性のプロパティをサブクラスで readonly にしたいのですが、

@interface SuperClass : NSObject
@property (strong, nonatomic, readwrite) NSObject *variable;
@end
@interface SubClass : SuperClass
@property (strong, nonatomic, readonly) NSObject *variable; // 注意が出る
@end

 サブクラスのほうで注意が表示され、

Attribute ‘readonly’ of property ‘variable’ restricts attribute
‘readwrite’ of property inherited from ‘SuperClass’.

 サブクラスの実装でアクセスしてみると、やはり書き込みできてしまいます。

@implementation SubClass
- (void)method {
  self.variable = [[NSObject alloc] init]; // 書き込みできてしまう
}
@end

妥協案

 そのため、現在妥協案として以下のようにサブクラスではプロパティのセッタを無効にし、ドット記法でアクセスされた時のためにセッタの実装も空にしています。

@interface SubClass : SuperClass
- (void)setVariable:(NSObject *)variable __unavailable;
@end

@implementation SubClass
- (void)setVariable:(NSObject *)variable {
  return;
}
@end

 この動作を Objective-C の言語的に、スマートに実現できないでしょうか? この方法だと readonly にしたいプロパティへのドット記法での書き込みに対して何の警告も出せないのが不親切で困っています。

環境

Xcode 5.1.1 / iOS 7.1 SDK

解決

ドット記法の書き込みについてコンパイル時に警告またはエラーを出す方法はないと思います。

代案1

スーパークラスが readonly なプロパティを持つようにし、サブクラスがそれを readwrite にする。

Cocoa ではふつう可変オブジェクトは不変オブジェクトのサブクラスとして実装されます。たとえば NSMutableArrayNSArray のサブクラスです。その逆は良い設計とは言えないので、スーパークラスの実装を変えられる立場にあるなら、 Cocoa にならうのが確実だと思います。

代案2

readonly なプロパティを持つクラスを作り、 SuperClass と SubClass をそこから派生する:

@interface BaseClass : NSObject
@property (strong, nonatomic, readonly) NSObject *variable;
@end

@interface SuperClass : BaseClass
@property (strong, nonatomic, readwrite) NSObject *variable;
@end

@interface SubClass : BaseClass
@end

代案1のように変更できない事情があり、かつ SuperClass と SubClass が実際には親子関係になくてもよいなら、こうすることもできます。

-isKindOfClass: などを上書きすることで SuperClass と SubClass を親子のように見せかけることも(ある程度は)できますが、ここでは深入りしません。

代案3

NSAssert で実行時にエラーを出す:

@implementation SubClass
- (void)setVariable:(NSObject *)variable {
  NSAssert(NO, @"-[SubClass setVariable:] should not be used");
  return;
}
@end

代案4

variable プロパティ自体を unavailable にしてコンパイルエラーにし、 readonly なプロパティを別途用意する:

@interface SubClass : SuperClass
@property (strong, nonatomic, readwrite) NSObject *variable __unavailable;
@property (strong, nonatomic, readonly) NSObject *readonlyVariable;
@end

@implementation SubClass
- (NSObject *)readonlyVariable { return _variable; }
@end

ただしこの方法ではスーパークラスにキャストすることでコンパイルエラーを回避できてしまいます:

aSubClass.variable = @42;  // コンパイルエラー
((SuperClass *)aSubClass).variable = @42; // OK
回答者: Anonymous

Leave a Reply

Your email address will not be published. Required fields are marked *