8

说我有这门课

@interface CustomClass : NSObject

@property (nonatomic, strong) NSArray * nicestArrayEver;

@end

我想创建一个CustomClass的子类,但这里有一个问题

@interface ASubClassCustomClass : CustomClass

@property (nonatomic, strong) NSMutableArray * nicestArrayEver;

@end

您可以想象的问题是,当我初始化 ASubClassCustomClass 并将其称为超级初始化程序(因为需要其他属性)时,创建了不可变的 nicestArrayEver .. 我怎样才能避免它的创建,以便我可以设置可变的?

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

4

4 回答 4

6

您可以通过使用不同的支持变量使其工作,合成时看起来像这样:@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

另一种方法可能是在基类中有 2 个 init 方法,一个将实例化属性,一个不会,但将任务留给子类 - 这将防止您创建昂贵的对象只是为了扔掉它们离开。
现在,基类可以直接使用第二个 init 实例化并进入错误状态。您可以通过检查 self 类类型来避免这种情况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;
}
于 2012-05-27T00:00:39.983 回答
1

我不明白您为什么要这样做,但我建议您执行以下操作:在您的子类中声明一个单独的 NSMutableArray 属性(我们称之为 nicestMutableArrayEver)并覆盖您的超类 NSArray 属性的 getter 以返回mutableArray 实例:

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

这样,每当您引用超类属性时,您都可以取回 mutableArray。

最好的,

于 2012-05-27T00:32:01.593 回答
1

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

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

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

无论属性是否在外部可变,支持实例变量可能需要是可变的。类可能需要纯粹在内部改变它自己的属性。

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

Alternatively, the base class initializer(s) could call out to another method to obtain the array. The base class implementation would return an NSArray. The subclass could override that method to return an NSMutableArray.

于 2012-05-27T00:45:21.217 回答
-1

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

实际上只有两个原因我可以看到做你要求的事情:

  1. 为了避免额外的开销NSMutableArray
  2. 为了防止CustomClass修改数组的内容,除非它是一个ASubClassCustomClass.

虽然这些都是很好的目标,但我想说在这种情况下,稍微简化一下是值得的。

于 2012-05-26T23:41:29.247 回答