3

readonly として継承され、継承されたクラスで readwrite として再宣言されたプロパティを操作しているときに、奇妙な動作を発見しました。

イン・アー

@interface A : NSObject

@property (nonatomic, strong, readonly) NSObject * someProperty;

@end

Bh

@interface B : A

// no matter if here
// @property (nonatomic, strong, readwrite) NSObject * someProperty;

- (void)foo;

@end

Bm

@interface B()

// no matter if here
@property (nonatomic, strong, readwrite) NSObject * someProperty;

@end

@implementation B

- (void)foo {

    NSLog(@"%@", self.someProperty);

    // crash here with unrecognized selector setSomeProperty:
    self.someProperty = [NSObject new];
}

@end

呼び出し

self.someProperty = [NSObject new];

認識されないセレクター「setSomeProperty:」でコードがクラッシュします。

調査によると、読み取り書き込みとして宣言されていても、セッターが合成されていないように見えます

なぜこうなった?コンパイラは、これが発生することに対する警告を示しませんでした。また、この動作が文書化されている場所も見つかりませんでした

4

2 に答える 2

2

Bm ファイルにディレクティブを追加する@synthesizeと、クラッシュはなくなります。

@synthesize someProperty = _someProperty;

問題は、親クラスでプロパティを宣言したreadonlyため、合成されたセッターがないことです。そして、サブクラスはこの動作を継承します。プロパティをreadwriteサブクラスに再宣言しても。この@synthesizeコマンドは、クラス B のアクセサ メソッドを再度生成するようにコンパイラに指示します。

お役に立てれば!

ここに画像の説明を入力

于 2013-06-19T18:14:38.260 に答える
2

公式のリファレンスを提供することはできませんが、これは私が経験したことです。スーパークラスから継承されたプロパティの場合、コンパイラはアクセサー メソッドを生成しません。

あなたの場合、プロパティはreadonlyクラス A のように宣言されているため、コンパイラは getter メソッドのみを作成します。クラス B での再宣言は、追加のアクセサー メソッドを作成しません。その理由は、クラス B がクラス A でプロパティが「どのように」実現されるかを知らないためかもしれません (インスタンス変数である必要はありません)。

したがって、サブクラスでのプロパティ宣言は、getter および setter 関数が実行時に使用可能になるというコンパイラに対する "約束" にすぎません (@dynamic 宣言と同様)。セッターがない場合、実行時例外が発生します。

したがって、サブクラスでプロパティを再宣言するユース ケースは、スーパークラスがパブリックインターフェイスでプロパティを読み取り専用として宣言し、( private ) クラス拡張では読み書き可能として宣言する場合です。

// A.h
@interface A : NSObject
@property (nonatomic, strong, readonly) NSObject * someProperty;
@end

// A.m
@interface A()
@property (nonatomic, strong, readwrite) NSObject * someProperty;
@end

@implementation A
@end

この場合、setter と getter の両方がクラス A で作成され、クラス B はその実装でプロパティを読み書き可能として再宣言できます。

于 2013-06-19T18:20:11.170 に答える