سؤال

Say I have a pseudo-abstract base class that users should not instantiate. Basically I want to throw a warning when they're trying to call init on the class, or return one of the concrete instances with default values.

However, the concrete implementations of that base class have to call [super init] in their initializers. That should of course be allowed.

How would I best go about this?

I was thinking that this should be fine:

@implementation KTPhysicsShape
-(id) init
{
    // throw exception here or return concrete instance with default values
}

// this is what subclasses would call in place of [super init]:
-(id) internal_initFromSubclass
{
    return [super init];
}
@end

Any concerns about this approach? I know others could still call the internal method, but I'm mostly concerned about disallowing init since that's what users would try to call foremost.

هل كانت مفيدة؟

المحلول

I have also worked at the problem of how to have effectively abstract classes, but I'm not that into this solution. It seems to me that it's going to make your subclass code look weird and harder to read for casual observers.

If you require that your subclasses do particular initialization in -init, yours may the only solution. But if you just want to ensure that they have subclassed, you can do that within -init:

-(id) init
{
    NSAssert(![self isMemberOfClass:[KTPhysicsShape class]], 
                                         @"KTPhysicsShape must be subclassed!");
    return [super init];
}

نصائح أخرى

This indicates that your architecture has a serious flaw. The whole point of the designated initializer chain is that it can be executed in a predictable order without variation. Adding contractual obligations to the subclasses to not follow the normal chain adds fragility and unneeded complexity.

The crux of the flaw is that you have an abstract class that doesn't appear to be truly abstract; it can have concrete instances and that requires concrete initialization.

First, why can't you break the class into a truly abstract class and a concrete class?

If you can't (or don't want to -- certainly, more classes has costs of its own), then one solution is to break out the commonly used initialization operations into a separate method:

- (void) commonKTPhysicsShapeInit
{
    ....
}

That does not call super. This would not be declared in your header; it is an internal-to-implementation-only method, thus the name.

Then, let your subclasses call through the standard designated initializer that calls commonInit. For concrete instances of that class, have a separate initializer that both calls commonInit and does the concrete initialization dance.

It is similar to what you proposed, but presents the interface in a fashion that follows existing patterns more closely.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top