4234


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

[code]

    @interface SuperClass : NSObject
    @property (strong, nonatomic, readwrite) NSObject *variable;
    @end
[/code]

[code]

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

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

> Attribute 'readonly' of property 'variable' restricts attribute 'readwrite'
> of property inherited from 'SuperClass'.

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

[code]

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

**妥協案**

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

[code]

    @interface SubClass : SuperClass
    - (void)setVariable:(NSObject *)variable __unavailable;
    @end
    
    @implementation SubClass
    - (void)setVariable:(NSObject *)variable {
      return;
    }
    @end
[/code]

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

**環境**

Xcode 5.1.1 / iOS 7.1 SDK


CC BY-SA 3.0
2015-01-06T16:33:00.437
3214
2015-01-07T08:19:13.040
question
objective-c
スーパークラスで読み書き可能なプロパティをサブクラスで読み取り専用にできますか？


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

## 代案1

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

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

## 代案2

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

[code]

    @interface BaseClass : NSObject
    @property (strong, nonatomic, readonly) NSObject *variable;
    @end
    
    @interface SuperClass : BaseClass
    @property (strong, nonatomic, readwrite) NSObject *variable;
    @end
    
    @interface SubClass : BaseClass
    @end
    
[/code]

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

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

## 代案3

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

[code]

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

## 代案4

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

[code]

    @interface SubClass : SuperClass
    @property (strong, nonatomic, readwrite) NSObject *variable __unavailable;
    @property (strong, nonatomic, readonly) NSObject *readonlyVariable;
    @end
    
    @implementation SubClass
    - (NSObject *)readonlyVariable { return _variable; }
    @end
    
[/code]

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

[code]

    aSubClass.variable = @42;  // コンパイルエラー
    ((SuperClass *)aSubClass).variable = @42; // OK
    
[/code]


CC BY-SA 3.0
2015-01-07T08:19:13.040
4234
2015-01-07T08:19:13.040
2259
3214
answer