2012-05-26 8 views
7

Dire che ho questa classeCreare sottoclasse con differenti proprietà sottoclasse

@interface CustomClass : NSObject 

@property (nonatomic, strong) NSArray * nicestArrayEver; 

@end 

E voglio creare una sottoclasse di CustomClass, ma qui è la cattura

@interface ASubClassCustomClass : CustomClass 

@property (nonatomic, strong) NSMutableArray * nicestArrayEver; 

@end 

La questione, come si può immaginare è che quando inizializzo ASubClassCustomClass e chiamo il suo superinizializzatore (dato che ci sono altre proprietà richieste) viene creato l'inmutable nicestArrayEver .. come posso evitare la sua creazione così posso impostare quella mutabile?

Nota: questo è solo un esempio, la vera implementazione richiede una sottoclasse pesante per creare e davvero personalizzata (non è un NSArray).

risposta

5

È possibile farlo funzionare, utilizzando diverse variabili backing, quando sintetizzando sembra che questo: @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; 
} 

uscita

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

Un altro approccio potrebbe essere quello di avere 2 metodi init nella classe base, uno, che istanzia la proprietà e uno, che non lo farà, ma lascia quell'attività per il bambino c lass: questo ti impedirà di creare oggetti costosi solo per buttarli via.
Ora la classe base può essere istanziata direttamente con il secondo init e passare a uno stato falso. È possibile evitare ciò controllando il tipo di classe autonoma con isMemberOfClass: e generando un errore, se il tipo di classe è la classe base.

@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 per rispondere alla domanda e per dimostrarmi facilmente di sbagliare. :) –

-1

Non puoi davvero farlo. Basta creare CustomClass con NSMutableArray. Potresti crearli come tipo id e controllare isKindOfClass: ma questo è solo un lavoro ingrato e non proprio necessario.

Ci sono solo due motivi che ho potuto vedere a fare quello che stai chiedendo:

  1. per evitare il sovraccarico aggiuntivo di NSMutableArray
  2. Per evitare CustomClass di modificare i contenuti della matrice meno che non sia a ASubClassCustomClass.

Mentre questi sono buoni obiettivi, direi che in questo caso vale la pena semplificare un po '.

+0

Josh, per favore vedi la nota che ho appena aggiunto. Sono d'accordo con te, se fosse un NSArray, ma in questo caso, non lo è, ed è pesante. –

+0

In entrambi i casi, sei ancora nella stessa barca. Potrebbe valere la pena di riesaminare il tuo design. Se un bambino ha un diverso tipo di informazione rispetto alla sua superclasse, probabilmente vale la pena archiviare in una variabile diversa. Se si desidera, è possibile creare un metodo di classe che restituisca la variabile appropriata in base e sovrascriva quella nella sottoclasse. –

+0

È infatti assolutamente possibile avere una proprietà con lo stesso nome ma un tipo diverso in una sottoclasse. L'unico problema è che (a meno che la visibilità dell'ulivo della superclasse non sia stata cambiata) l'ivar di supporto deve avere un nome diverso. Vedi http://stackoverflow.com/a/10690692/ o la risposta di vikingosegundo a questa stessa domanda. –

1

non vedo il motivo per cui si vorrebbe farlo, ma io vi consiglio di fare come segue: nella sottoclasse dichiarare una proprietà NSMutableArray separato (chiamiamolo nicestMutableArrayEver) e sovrascrivere il getter per la vostra proprietà NSArray superclasse per tornare l'istanza mutableArray:

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

in questo modo, si potrebbe ottenere indietro il vostro mutableArray ogni volta che fa riferimento alla proprietà superclasse.

Best,

+0

Non vedo perché qualcuno non vuole farlo. Questa relazione tra CustomClass e ASubClassCustomClass è una specializzazione da un caso comune a un caso più speciale, uno schema molto tipico nella progettazione orientata agli oggetti. Nota anche che con il tuo approccio, i lanci saranno necessari costantemente anche per chiudere gli avvertimenti del compilatore. – vikingosegundo

1

Le proprietà non dovrebbero quasi mai essere di tipo mutabile.Se lo fanno, quindi un chiamante può ottenere il puntatore e mutare le proprietà dell'oggetto dietro la sua schiena. Se una proprietà dovrebbe essere esternamente mutabile, ciò dovrebbe avvenire attraverso metodi di mutazione.

Ricordare che le proprietà definiscono l'interfaccia, non l'implementazione. @synthesize può creare un'implementazione dalla dichiarazione di proprietà, ma se ciò dovesse fare la cosa sbagliata non dovresti usarlo.

Quindi, il primo passo è definire l'interfaccia per la classe e la sottoclasse, senza riguardo per l'implementazione. Solo dopo aver saputo quali dovrebbero essere le interfacce dovresti progettare l'implementazione di ciascuna.

Indipendentemente dal fatto che una proprietà sia esternamente modificabile, potrebbe essere necessario modificare la variabile di istanza di backup. La classe potrebbe aver bisogno di mutare la propria proprietà puramente internamente.

Si potrebbe rendere la classe base hanno un inizializzatore designato che prende l'oggetto array come parametro (di tipo id affinché sostituzione della sottoclasse non deve lanciare a considerarlo NSMutableArray*). Quindi, gli inizializzatori "normali" della classe chiamerebbero quell'iniziatore designato con lo NSArray da utilizzare. L'inizializzatore designato della sottoclasse chiamerebbe l'inizializzatore designato della superclasse, passando in NSMutableArray da usare.

In alternativa, gli inizializzatori della classe base possono chiamare un altro metodo per ottenere l'array. L'implementazione della classe base restituirebbe un NSArray. La sottoclasse potrebbe sovrascrivere quel metodo per restituire uno NSMutableArray.