2012-05-26 2 views
7

とサブクラスを作成し、あなたが想像できるような問題がある私は、このクラスに異なるサブクラスプロパティ

@interface CustomClass : NSObject 

@property (nonatomic, strong) NSArray * nicestArrayEver; 

@end 

を持っていると私はCustomClassのサブクラスを作成したいと言うが、ここでキャッチです

@interface ASubClassCustomClass : CustomClass 

@property (nonatomic, strong) NSMutableArray * nicestArrayEver; 

@end 

私がASubClassCustomClassを初期化してスーパー初期化子を呼び出すと(他のプロパティが必要なので)、変更不可能なnicestArrayEverが作成されます。どのようにして作成できないようにして、変更可能なものを設定できますか?

注:これは単なる例であり、実際の実装では、作成するのに重く、実際にカスタマイズされたサブクラス(NSArrayではない)が呼び出されます。

答えて

5

それを合成すると、このようになりますときには、異なるバッキング変数を使用して、それを動作させることができます:出力

2012-05-27 01:59:16.221 NicestArray[2312:403] __NSArrayI 
2012-05-27 01:59:16.225 NicestArray[2312:403] __NSArrayM 

別のアプローチを持っている可能性があり@synthesize nicestArrayEver = nicestArrayEverSubClass_;

#import <Foundation/Foundation.h> 

@interface CustomClass : NSObject 

@property (nonatomic, strong) NSArray * nicestArrayEver; 

@end 

@implementation CustomClass 
@synthesize nicestArrayEver ; 

-(id)init 
{ 
    if (self = [super init]) { 
     nicestArrayEver = [[NSArray alloc] init]; 
    } 
    return self; 
} 
@end 

@interface ASubClassCustomClass : CustomClass 

@property (nonatomic, strong) NSMutableArray * nicestArrayEver; 

@end 

@implementation ASubClassCustomClass 
@synthesize nicestArrayEver = nicestArrayEverSubClass_; 

-(id)init{ 
    if (self = [super init]) { 
     nicestArrayEverSubClass_ = [[NSMutableArray alloc] init]; 
    } 
    return self; 
} 
@end 



int main(int argc, const char * argv[]) 
{ 

    @autoreleasepool { 

     CustomClass *c1 = [[[CustomClass alloc] init] autorelease]; 
     ASubClassCustomClass *c2 = [[[ASubClassCustomClass alloc] init] autorelease]; 

     NSLog(@"%@", NSStringFromClass([[c1 nicestArrayEver] class])); 
     NSLog(@"%@", NSStringFromClass([[c2 nicestArrayEver] class])); 

    } 
    return 0; 
} 

基本クラスの2つのinitメソッド、1つはプロパティをインスタンス化し、もう1つはそうではないが、そのタスクを子cに任せるlass - これはあなたが高価なオブジェクトを作成してそれらを投げ捨てるのを防ぎます。
これで、基本クラスが2番目のinitで直接インスタンス化され、false状態になることがありました。 isMemberOfClass:で自己クラス型をチェックすることでこれを回避し、クラス型が基本クラスであればエラーを投げることができます。

@interface CustomClass : NSObject 

@property (nonatomic, strong) NSArray * nicestArrayEver; 
-(id)initWithoutArray; 
@end 

@implementation CustomClass 
@synthesize nicestArrayEver ; 

-(id) initWithoutArray 
{ 
    if (self = [super init]) { 
     if ([self isMemberOfClass:[CustomClass class]]) { 
      [NSException raise:@"AbstractMethodCall" format:@"%@ should be called only from Subclasses of %@", NSStringFromSelector(_cmd), NSStringFromClass([self class])]; 
     } 
    } 
    return self; 
} 


-(id)init 
{ 
    if (self = [super init]) { 
     nicestArrayEver = [[NSArray alloc] init]; 
    } 
    return self; 
} 
@end 

@interface ASubClassCustomClass : CustomClass 

@property (nonatomic, strong) NSMutableArray * nicestArrayEver; 

@end 

@implementation ASubClassCustomClass 
@synthesize nicestArrayEver = nicestArrayEverSubClass_; 

-(id)init{ 
    if (self = [super initWithoutArray]) { 
     nicestArrayEverSubClass_ = [[NSMutableArray alloc] init]; 
    } 
    return self; 
} 

@end 



int main(int argc, const char * argv[]) 
{ 

    @autoreleasepool { 

     CustomClass *c1 = [[[CustomClass alloc] init] autorelease]; 
     ASubClassCustomClass *c2 = [[[ASubClassCustomClass alloc] init] autorelease]; 

     NSLog(@"%@", NSStringFromClass([[c1 nicestArrayEver] class])); 
     NSLog(@"%@", NSStringFromClass([[c2 nicestArrayEver] class])); 

     //this works, as it is the subclass 
     ASubClassCustomClass *shouldWork = [[[ASubClassCustomClass alloc] init] autorelease]; 

     // ouch! 
     CustomClass *shouldCrash = [[[CustomClass alloc] initWithoutArray] autorelease]; 

    } 
    return 0; 
} 
+1

+1質問に答えるために、そして間違って私を間違って証明するため。 :) –

-1

あなたは本当にそれを行うことはできません。 CustomClassNSMutableArrayを作成してください。それらをタイプidとして作成し、isKindOfClass:をチェックすることができますが、それはちょっとした手間であり、実際には必要ありません。

私はあなたが求めているものを行うために見ることができる実際には2つの理由があります。

  1. NSMutableArray
  2. の余分なオーバーヘッドを回避するために、それがある場合を除き、配列の内容を変更することからCustomClassを防ぐためには、 a ASubClassCustomClass

これらは良い目標ですが、この場合は少し簡略化する価値があります。

+0

ジョシュさん、ちょうど追加したノートをご覧ください。 私はそれがNSArrayだった場合、あなたに同意しますが、この場合はそうではなく、重いです。 –

+0

いずれにせよ、あなたはまだ同じボートにいます。それはあなたのデザインを再検討する価値があるかもしれません。子どもがスーパークラスとは異なるタイプの情報を持っている場合は、おそらく別の変数に格納する価値があります。あなたが望むなら、適切な変数を返すクラスメソッドを作成し、それをあなたのサブクラスでオーバーライドすることができます。 –

+0

サブクラスに同じ名前で異なる型を持つことは、実際には絶対に可能です。一つの捉え方は(スーパークラスのivarの可視性が変更されていない限り)バッキングivarは別の名前を持つ必要があります。この非常に疑問の答えは、http://stackoverflow.com/a/10690692/またはvikingosegundoの回答を参照してください。 –

1

私はあなたがすることをしたいと思う理由が表示されていないが、私は次のようにあなたがする助言:あなたのサブクラスで別のNSMutableArrayのプロパティ(のはnicestMutableArrayEverそれを呼びましょう)を宣言し、ゲッターをオーバーライドしますあなたのスーパークラスNSArrayのプロパティはmutableArrayインスタンスを返すようにするために:あなたはスーパークラスプロパティを参照するたびに

- (NSArray *)nicestArrayEver { 
    return [self nicestMutableArrayEver]; 
} 

この方法では、あなたのmutableArrayを取り戻すことができました。

ベスト、

+0

私は、なぜ誰かがそれをやりたいとは思わない。 CustomClassとASubClassCustomClassの関係は、一般的なものから特殊なものへの特殊化です。オブジェクト指向設計における非常に典型的なパターンです。また、あなたのアプローチでは、キャストは常にコンパイラの警告を止める必要があることにも注意してください。 – vikingosegundo

1

ほとんどの場合、プロパティに変更可能なタイプはありません。そうであれば、呼び出し元はポインタを取得し、その背後でオブジェクトのプロパティを変更することができます。プロパティを外部的に変更可能にする必要がある場合は、それは突然変異メソッドを使用する必要があります。

プロパティは、実装ではなくインターフェイスを定義していることに注意してください。 @synthesizeは、プロパティ宣言から実装を作成することができますが、それが間違ったことをするなら、それを使用すべきではありません。

最初の手順は、実装に関係なく、クラスとサブクラスのインターフェイスを定義することです。インターフェイスの内容が分かったら、それぞれの実装を設計する必要があります。

プロパティが外部的に変更可能であるかどうかに関係なく、バッキングインスタンス変数は変更可能である必要があります。クラスは、内部的に独自のプロパティを変更する必要があります。

サブクラスのオーバーライドをキャストしてNSMutableArray*として扱う必要がないように、配列オブジェクトをパラメータ(idのタイプ)とする指定された初期化子をベースクラスに作成させることができます。次に、クラスの「通常の」イニシャライザは、指定されたイニシャライザをNSArrayで呼び出して使用します。サブクラスの指定された初期化子は、スーパークラスの指定された初期化子を呼び出し、使用するためにNSMutableArrayを渡します。

また、基本クラス初期化子は、別のメソッドを呼び出して配列を取得することもできます。基本クラスの実装はNSArrayを返します。サブクラスは、そのメソッドをオーバーライドしてNSMutableArrayを返すことができます。

関連する問題