19

次のようなクラスがあります (簡潔にするためにインポートを省略しています)。

Base.h:

@interface Base : NSObject
@property (strong, readonly) NSString *something;
- (id)initWithSomething:(NSString *)something;
@end

Base.m:

@implementation Base
- (id)initWithSomething:(NSString *)something {
    self = [super init];
    if (self) _something = something;
    return self;
}
@end

ご覧のとおり、「something」プロパティは読み取り専用です。ここで、そのプロパティをオーバーライドして書き込み可能にするサブクラスを作成したいと思います。

サブ h:

@interface Sub : Base
@property (strong) NSString *something;
@end

サブメートル:

@implementation Sub
@end

そしてコード:

main.c:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Sub *o = [Sub new];
        o.something = @"foo";
        NSLog(@"%@", o.something);
    }
    return 0;
}

このコードの結果は次のとおりです。

    2013-09-07 13:58:36.970 ClilTest[3094:303] *** Terminating app due to uncaught
    exception 'NSInvalidArgumentException', reason: '-[Sub setSomething:]: unrecognized
    selector sent to instance 0x100109ff0'

何故ですか?setSelector が見つからないのはなぜですか?

代わりにサブクラスでこれを行うと:

サブメートル:

@implementation Sub
@synthesize something = _something;
@end

それはすべて動作します。@propertyこれは、サブクラスのプロパティが のように定義されていても、デフォルトでは合成されないということ@interfaceですか? コンパイルは、Base から自動的に生成されたゲッターを何らかの形で「認識」し、セッターを生成しませんか? なぜ、セッターはまだ存在しないので生成する必要があると思います。私は Xcode 4.6.2 を使用しており、プロジェクトは Cli ツール (タイプ Foundation) ですが、iPhone アプリである実際のプロジェクトでも同じことが起こります。

背景: 一部の機器への Bluetooth 接続を必要とする重いオブジェクト (Base のインスタンス) があり、一部の機能のためにビュー コントローラーを作成することになっています。テストを簡単にするために、BT に接続したくありません (実際には、物理​​デバイスが必要であり、その上でコードをテストする必要があります)。シミュレーターでテストできるようにしたいと考えています。私が思いついたのは、いくつかのメソッド/プロパティをスタブ化して代わりに使用するサブクラス (Sub) を作成するだけで、コードの準備ができたら、サブクラスのコードを削除し、そのインスタンスを正しいものに置き換えるだけです。デバイスでテストし、コミットしてプッシュします。上記の奇妙なことを除いて、実際には正常に動作し@propertyます。

プロパティのオーバーライドで何が起こっているのか誰か教えてもらえますか?

4

3 に答える 3

31

読み取り専用プロパティの場合、getter メソッドのみが合成され、setter メソッドは合成されません。

また、サブクラスをコンパイルするとき、コンパイラはプロパティが基本クラスでどのように実現されるかを知りません (バッキング インスタンス変数ではなく、カスタム ゲッターである可能性があります)。したがって、サブクラスでセッター メソッドを作成することはできません。


サブクラスから同じインスタンス変数への書き込みアクセスが必要な場合@protectedは、(サブクラスでアクセスできるように) 基本クラスと同様に宣言し、サブクラスでプロパティを読み書き可能として再宣言する必要があります。セッターメソッドを提供します:

Base.h:

@interface Base : NSObject {
@protected
    NSString *_something;
}
@property (strong, readonly) NSString *something;
- (id)initWithSomething:(NSString *)something;
@end

サブ h:

@interface Sub : Base
@property (strong, readwrite) NSString *something;
@end

サブメートル:

@implementation Sub
-(void)setSomething:(NSString *)something
{
    _something = something;
}
@end

あなたのソリューション

@synthesize something = _something;

サブクラスで別のインスタンス変数 を使用して、サブクラスで getter メソッドと setter メソッドを生成します_something(基本クラスとは異なり_somethingます)。

self.somethingこれも同様に機能します。基本クラスとサブクラスで異なるインスタンス変数を参照していることに注意してください。これをより明確にするために、サブクラスで別のインスタンス変数を使用できます。

@synthesize something = _somethingElse;
于 2013-09-07T12:56:37.280 に答える