2012-05-26 16 views
7

Powiedz, mam tej klasyTworzenie podklasy z różnymi własności podklasy

@interface CustomClass : NSObject 

@property (nonatomic, strong) NSArray * nicestArrayEver; 

@end 

I chcę utworzyć podklasę CustomClass, ale tu jest haczyk

@interface ASubClassCustomClass : CustomClass 

@property (nonatomic, strong) NSMutableArray * nicestArrayEver; 

@end 

Kwestia, jak można sobie wyobrazić, jest że po zainicjowaniu ASubClassCustomClass i wywołaniu jego superinicjalizatora (ponieważ wymagane są inne właściwości), tworzony jest inmutable nicestArrayEver .. jak mogę go uniknąć, więc mogę ustawić zmienną?

Uwaga: To tylko przykład, prawdziwa implementacja wymaga ciężkiej do stworzenia i naprawdę niestandardowej podklasy (nie jest to NSArray).

Odpowiedz

5

Można zrobić to praca, za pomocą różnych zmiennych podłożach, gdy syntetyzowania wygląda to tak: @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; 
} 

wyjście

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

Innym rozwiązaniem mogłoby być posiadanie 2 metody init w klasie bazowej, jedna, która stworzy instancję i jedną, która nie będzie, ale pozostawia to zadanie dla dziecka c lass - to uniemożliwi ci tworzenie drogich przedmiotów po prostu je wyrzucasz.
Teraz klasa bazowa może uzyskać instancję bezpośrednio z drugim init i przejść do stanu fałszywego. Można tego uniknąć, sprawdzając typ własnej klasy za pomocą isMemberOfClass: i zgłaszając błąd, jeśli typ klasy jest klasą podstawową.

@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 za udzielenie odpowiedzi na pytanie i za łatwe udowodnienie mi, że się mylę. :) –

-1

Naprawdę nie możesz tego zrobić. Po prostu stwórz CustomClass z NSMutableArray. Można je utworzyć jako typ id i sprawdzić isKindOfClass:, ale to tylko uciążliwe i niekoniecznie konieczne.

Tak naprawdę są tylko dwa powody widziałem zrobić to pytasz:

  1. Aby uniknąć dodatkowego obciążania NSMutableArray
  2. Aby zapobiec CustomClass z modyfikując zawartość tablicy, chyba że jest a ASubClassCustomClass.

Chociaż są to dobre cele, powiedziałbym, że w tym przypadku warto nieco je uprościć.

+0

Josh, zobacz notatkę, którą właśnie dodałem. Zgadzam się z Tobą, jeśli to był NSArray, ale w tym przypadku nie jest i jest ciężki. –

+0

Tak czy inaczej, wciąż jesteś w tej samej łodzi. Być może warto ponownie przeanalizować swój projekt. Jeśli dziecko ma inny typ informacji niż jego nadklasa, prawdopodobnie warto go zapisać w innej zmiennej. Jeśli chcesz, możesz stworzyć metodę klasy, która zwróci odpowiednią zmienną i zastąpi ją w twojej podklasie. –

+0

Jest w gruncie rzeczy absolutnie możliwe posiadanie właściwości o tej samej nazwie, ale innego typu w podklasie. Jedyny haczyk polega na tym, że (chyba że zmieniono widoczność ivar warstwy nadrzędnej), to ivar musi mieć inną nazwę. Zobacz odpowiedź http://stackoverflow.com/a/10690692/ lub Vikingosegundo na to pytanie. –

1

ja nie widzę powodu, dlaczego chcesz to zrobić, ale radzę Ci zrobić w następujący sposób: w podklasie zadeklarować odrębną nieruchomość NSMutableArray (nazwijmy go nicestMutableArrayEver) i zastąpić getter za swoją własność NSArray nadklasą powrócić instancję mutableArray:

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

w ten sposób można otrzymać zwrot mutableArray dowolnym momencie odwołać właściwość nadrzędnej.

Najlepsze,

+0

Nie widzę, dlaczego ktoś nie chce tego zrobić. Ta relacja między CustomClass i ASubClassCustomClass jest specjalizacją od wspólnej do bardziej specjalnego przypadku - bardzo typowy wzór w projekcie zorientowanym obiektowo. Zauważ również, że przy twoim podejściu będą potrzebne odlewane stale zbyt zamknięte ostrzeżenia kompilatora. – vikingosegundo

1

Właściwości prawie nigdy nie powinny mieć zmiennego typu.Jeśli tak, to wywołujący może uzyskać wskaźnik i zmutować właściwości obiektu za jego plecami. Jeśli właściwość powinna podlegać mutacjom zewnętrznym, powinna to być metoda mutacji.

Pamiętaj, że właściwości definiują interfejs, a nie implementację. @synthesize może utworzyć implementację z deklaracji właściwości, ale jeśli zrobiłoby to niewłaściwą rzecz, nie powinieneś jej używać.

Pierwszym krokiem jest zdefiniowanie interfejsu dla klasy i podklasy bez względu na implementację. Dopiero gdy wiesz, jakie powinny być interfejsy, zaplanuj implementację każdego z nich.

Niezależnie od tego, czy właściwość ma zewnętrzną zmienność, zmienna instancji kopii może wymagać zmiany. Klasa może potrzebować mutować swoją własność wyłącznie wewnętrznie.

Można spowodować, że klasa podstawowa będzie miała określony inicjator, który pobiera obiekt tablicy jako parametr (typu id, tak aby nadpisanie klasy podklasy nie było wymagane, aby potraktować ją jako NSMutableArray*). Następnie "normalne" inicjatory klasy wywoływałyby ten inicjator za pomocą NSArray do użycia. Wyznaczony inicjator podklasy nazwałby inicjator nadklasy jako inicjalizatorem, przekazując do użycia NSMutableArray.

Alternatywnie inicjator (-y) bazowy może wywołać inną metodę uzyskiwania tablicy. Implementacja klasy podstawowej zwróci wartość NSArray. Podklasa może zastąpić tę metodę, aby zwrócić wartość NSMutableArray.