2012-05-26 134 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

你可以把它的工作,通過使用不同的後盾變量,當合成它看起來像這樣:@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; 
} 

輸出

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

另一種方法可能是有基類中有兩個初始化方法,一個是實例化屬性,另一個是不會的,但是將該任務留給子c lass - 這會阻止你創造昂貴的物品,只是爲了扔掉它們。
現在基類可以直接使用第二個初始化實例化,並進入錯誤狀態。您可以通過使用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:但這只是一件苦差事,並不是真的有必要。

有真正的原因只有兩個,我可以看到你在問什麼:

  1. 爲了避免NSMutableArray
  2. 額外的開銷要防止CustomClass修改該數組的內容,除非它是一個ASubClassCustomClass

雖然這些都是很好的目標,但我認爲在這種情況下,值得簡化一下。

+0

喬希,請參閱我剛剛添加的註釋。 我同意你的觀點,如果它是一個NSArray,但在這種情況下,不是,而且很重。 –

+0

無論哪種方式,你仍然在同一條船上。這可能是值得重新審視你的設計。如果一個孩子的信息類型不同於其超類,那麼可能值得存儲在另一個變量中。如果你想要,你可以創建一個類方法,該方法返回相應的變量,並覆蓋子類中的變量。 –

+0

實際上,絕對有可能在子類中擁有一個具有相同名稱但類型不同的屬性。唯一的缺點是(除非超級巨人的伊娃變得可見),支持伊娃必須有不同的名字。看到http://stackoverflow.com/a/10690692/或vikingosegundo的這個問題的答案。 –

1

我不明白你爲什麼會想這樣做的原因,但我會建議你做如下:在子類中聲明一個單獨的NSMutableArray屬性(姑且稱之爲nicestMutableArrayEver)和覆蓋吸爲您的NSArray超屬性返回mutableArray實例:

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

通過這種方式,你可以只要你引用父財產取回您的mutableArray。

最好,

+0

我不明白,爲什麼有人不想這樣做。 CustomClass和ASubClassCustomClass之間的這種關係是一個從普通到特殊情況的專業化 - 在面向對象設計中的一個非常典型的模式。還要注意,在你的方法中,不斷需要強制關閉編譯器警告。 – vikingosegundo

1

屬性應該幾乎不會有可變類型。如果他們這樣做,那麼調用者可以獲得指針並將對象的屬性改變爲背後的屬性。如果一個屬性應該是外部可變的,那應該是通過變異方法。

請記住,屬性定義接口,而不是實現。 @synthesize可以從屬性聲明中創建實現,但如果這樣做會做錯誤的事情,你不應該使用它。

因此,第一步是爲您的類和子類定義接口,而不考慮實現。只有當你知道接口應該是什麼時,你應該設計每個接口的實現。

無論屬性是否是外部可變的,支持實例變量可能都需要是可變的。該類可能需要純粹內部改變其自己的屬性。

您可以讓基類有一個指定的初始值設定項,它將數組對象作爲參數(類型爲id,以便子類的重寫不必轉換爲將其視爲NSMutableArray*)。然後,該類的「正常」初始化程序將使用NSArray來調用指定的初始化程序。子類的指定初始化程序將調用超類的指定初始化程序,傳入NSMutableArray以供使用。

或者,基類初始化程序可以調用另一個方法來獲取數組。基類實現將返回NSArray。該子類可以覆蓋該方法以返回NSMutableArray