创建具有不同子类属性的子类

时间:2012-05-26 23:37:56

标签: objective-c cocoa subclass

说我有这个班级

@interface CustomClass : NSObject

@property (nonatomic, strong) NSArray * nicestArrayEver;

@end

我想创建一个CustomClass的子类,但这里是catch

@interface ASubClassCustomClass : CustomClass

@property (nonatomic, strong) NSMutableArray * nicestArrayEver;

@end

你可以想象的问题是,当我初始化ASubClassCustomClass并调用它的超级初始化器(因为还需要其他属性)时,会创建不可变的nicestArrayEver ..我怎样才能避免它的创建,所以我可以设置可变的?< / p>

注意:这只是一个例子,真正的实现调用了很重要的创建和真正定制的子类(不是NSArray)。

4 个答案:

答案 0 :(得分: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

另一种方法可能是在基类中有两个init方法,一个用于实例化属性,一个用于实例化属性,另一个用于子类,这将阻止你创建昂贵的对象扔掉它们。
现在,基类可以直接使用第二个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)

我没有看到你想要这样做的原因,但我建议你这样做:在你的子类中声明一个单独的NSMutableArray属性(让我们称之为nicestMutableArrayEver)并覆盖你的超类NSArray的getter返回mutableArray实例的属性:

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

这样,只要引用超类属性,就可以恢复mutableArray。

最佳,

答案 2 :(得分:1)

属性应该几乎从不具有可变类型。如果他们这样做,那么调用者可以获得指针并在其背后改变对象的属性。如果一个属性应该是外部可变的,那应该是通过变异方法。

请记住,属性定义接口,而不是实现。 @synthesize可以从属性声明中创建实现,但如果这样做会出错,则不应该使用它。

因此,第一步是为您的类和子类定义接口,而不考虑实现。只有在知道接口应该是什么之后,才应该设计每个接口的实现。

属性是否是外部可变的,后备实例变量可能需要是可变的。该类可能需要纯粹在内部改变自己的属性。

您可以使基类具有指定的初始化程序,该初始化程序将数组对象作为参数(类型为id),以便子类的覆盖不必强制转换为将其视为NSMutableArray* )。然后,类的“普通”初始值设定项将使用NSArray调用指定的初始值设定项。子类的指定初始值设定项将调用超类的指定初始值设定项,并传入NSMutableArray以使用。

或者,基类初始值设定项可以调用另一个方法来获取数组。基类实现将返回NSArray。子类可以覆盖该方法以返回NSMutableArray

答案 3 :(得分:-1)

你真的不能这样做。只需使用CustomClass创建NSMutableArray即可。您可以将它们创建为id类型并检查isKindOfClass:,但这只是一项苦差事而且并非真正必要。

我真的只有两个理由可以看到你要问的事情:

  1. 避免NSMutableArray
  2. 的额外开销
  3. 防止CustomClass修改数组的内容,除非它是ASubClassCustomClass
  4. 虽然这些都是很好的目标,但我会说在这种情况下,简化一下是值得的。