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