Initializing a class using superclass initializer

后端 未结 1 775
面向向阳花
面向向阳花 2021-02-14 22:53

I have got two classes, one a subclass of the other (say Animal and Dog). The superclass has got some initializers (say initAnimal), the s

1条回答
  •  傲寒
    傲寒 (楼主)
    2021-02-14 23:05

    Generally in Objective-C you create a designated initializer for each class and then subclasses use the same initializer. So instead of using initAnimal and initDog, you just use init. The dog subclass would then define its own init method and call the designated initializer in its parent class:

    @implementation Dog
    -(id)init
    {
        if( (self = [super init]) ) {  // call init in Animal and assign to self
            // do something specific to a dog
        }
        return self;
    }
    @end
    

    You don't really have to specify initDog and initAnimal because the class is declared on the right hand side of the assignment...

    Update: I'm adding the following to the answer to reflect the additional information in the question

    There are a number of ways to ensure that subclasses don't call initializers other than their designated initializer and the way you ultimately choose will be largely based on your whole design. One of the nice things about Objective-C is that it's so flexible. I will give you two examples here to get you started.

    First, if you create a subclass that has a different designated initializer than its parent class, you can overload the parent's initializer and throw an exception. This will let programmers know immediately that they've violated the protocol for your class... however, it should be stated that you should have a very good reason for doing this and that it should be very well documented that the subclass may not use the same initializer as the superclass.

    @implementation Dog
    -(id)init
    {
        // Dog does not respond to this initializer
        NSAssert( false, @"Dog classes must use one of the designated initializers; see the documentation for more information." );
    
        [self autorelease];
        return nil;
    }
    
    -(id)initWithFur:(FurOptionsType)furOptions
    {
        if( (self = [super init]) ) {
            // do stuff and set up the fur based on the options
        }
        return self;
    }
    @end
    

    Another way to do it is to have initializer more like your original example. In that case, you could change the default init on the parent class to always fail. You could then create a private initializer for your parent class and then make sure everyone calls the appropriate initializer in subclasses. This case is obviously more complicated:

    @interface Animal : NSObject
    -(id)initAnimal;
    @end
    
    @interface Animal ()
    -(id)_prvInitAnimal;
    @end
    
    @interface Dog : Animal
    -(id)initDog;
    @end
    
    @implementation Animal
    -(id)init
    {
        NSAssert( false, @"Objects must call designated initializers; see documentation for details." );
    
        [self autorelease];
        return nil;
    }
    
    -(id)initAnimal
    {
        NSAssert( [self isMemberOfClass:[Animal class]], @"Only Animal may call initAnimal" );
    
        // core animal initialization done in private initializer
        return [self _prvInitAnimal];
    }
    
    -(id)_prvInitAnimal
    {
        if( (self = [super init]) ) {
            // do standard animal initialization
        }
        return self;
    }
    @end
    
    @implementation Dog
    -(id)initDog
    {
        if( (self = [super _prvInitAnimal]) ) {
            // do some dog related stuff
        }
        return self;
    }
    @end
    

    Here you see the interface and implementation of the Animal and Dog class. The Animal is the designated top-level object and therefore overrides NSObject's implementation of init. Anyone who calls init on an Animal or any of Animal's subclasses will get an assertion error referring them to the documentation. Animal also defines a private initializer on a private category. The private category would stay with your code and subclasses of Animal would call this private initializer when they call up to super. It's purpose is to call init on Animal's superclass (NSObject in this case) and to do any generic initialization that might be necessary.

    Finally, the first line in Animal's initAnimal method is an assertion that the receiver is actually an Animal and not some subclass. If the receiver is not an Animal the program will fail with an assertion error and the programmer will be referred to the documentation.

    These are just two example of how you might design something with your specific requirements. However, I would strongly suggest you consider your design constraints and see if you really require this type of design as it's non-standard in Cocoa and in most OO design frameworks. For instance, you may consider making various animals root-level objects and just have an Animal protocol instead, requiring that all of the various "animals" respond to certain animal-generic messages. That way each animal (and true subclasses of Animal) can handle their designated initializers themselves and won't have to rely on superclasses behaving in such a specific, non-standard manner.

    0 讨论(0)
提交回复
热议问题