2008-11-10 9 views
7

J'ai deux classes, une sous-classe de l'autre (par exemple Animal et Dog). La superclasse a quelques initialiseurs (disons initAnimal), la sous-classe a quelques initialiseurs (disons initDog). Le problème est qu'il est parfaitement légal (du point de vue du compilateur) de faire quelque chose comme Dog *adog = [[Dog alloc] initAnimal], c'est à dire. initialise une classe en utilisant son initialiseur de superclasse. Je n'aime pas ça, parce que la sous-classe peut avoir des variables d'instance supplémentaires dont je veux m'assurer qu'elles sont initialisées. Un coup d'oeil dans le fichier d'en-tête résout cela, mais y a-t-il un moyen simple de faire vérifier le compilateur pour moi? J'ai un sentiment que je me manque quelque chose de terriblement évident, mais je ne peux pas mettre mon doigt dessus :-)Initialisation d'une classe à l'aide de l'initialiseur de superclasse

Mise à jour: Le initDog et initAnimal ne sont pas les meilleurs exemples. Je voulais dire deux initialisateurs vraiment différents (comme init pour Animal et initWithFur pour Dog). Si je voulais que chaque chien ait de la fourrure, j'aurais fait de la fourrure une partie de l'initialiseur, afin que personne ne puisse obtenir un objet chien sans fourrure. Mais alors il est toujours facile d'initialiser par erreur l'instance avec la superclasse , puis je suis arrosé.

Merci d'avoir mentionné les initialiseurs désignés, Jason. Cela ne m'est pas venu à l'esprit auparavant, mais je pourrais surcharger l'initialiseur désigné de la superclasse et y définir des valeurs par défaut saines. Mais je préférerais encore que je puisse rendre illégal l'utilisation d'autres initialiseurs que ceux de la classe elle-même - d'autres idées?

+0

Okay Zoul, J'ai fait des mises à jour à ma réponse en fonction des mises à jour de votre question ... c'est assez long maintenant, mais j'espère que cela vous donne une idée de ce que vous cherchiez. –

+0

Comme il s'agit d'un résultat supérieur. La réponse en XCode moderne, vous pouvez réaliser cela avec NS_UNAVAILABLE. Voir: http://stackoverflow.com/questions/195078/is-it-possible-to-make-the-init-method-private-in-objective-c#answer-27693034 – m4js7er

Répondre

18

Généralement dans Objective-C, vous créez un initialiseur désigné pour chaque classe, puis les sous-classes utilisent le même initialiseur. Donc, au lieu d'utiliser initAnimal et initDog, vous utilisez simplement init. La sous-classe de chien alors définir sa propre méthode d'initialisation et d'appeler l'initialiseur désigné dans sa classe parente:

@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 

Vous n'avez pas vraiment de préciser initDog et initAnimal parce que la classe est déclarée sur le côté droit de la cession ...

Mise à jour: J'ajoute ce qui suit à la réponse afin de refléter les informations supplémentaires dans la question

Il y a plusieurs façons de faire en sorte que les sous-classes ne remettent pas initializers autres que leur initialiseur désigné et la façon dont vous ultimat Le choix d'Ely sera basé en grande partie sur votre conception entière. L'une des bonnes choses à propos d'Objective-C, c'est que c'est tellement flexible. Je vais vous donner deux exemples pour vous aider à démarrer. En premier lieu, si vous créez une sous-classe ayant un initialiseur différent de celui de sa classe parente, vous pouvez surcharger l'initialiseur du parent et lancer une exception. Cela permettra aux programmeurs de savoir immédiatement qu'ils ont violé le protocole de votre classe ... cependant, il devrait être indiqué que vous devriez avoir une bonne raison de le faire et qu'il devrait être très bien documenté que la sous-classe peut n'utilise pas le même initialiseur que la superclasse.

@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 

Une autre façon de le faire est d'avoir un initialiseur plus comme votre exemple original. Dans ce cas, vous pouvez changer l'init par défaut de la classe parente pour toujours échouer. Vous pouvez ensuite créer un initialiseur privé pour votre classe parente, puis vous assurer que tout le monde appelle l'initialiseur approprié dans les sous-classes.Ce cas est évidemment plus compliqué:

@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 

Ici vous voyez l'interface et l'implémentation de la classe Animal et Chien. L'Animal est l'objet de premier niveau désigné et remplace donc l'implémentation de init de NSObject. Toute personne qui appelle init sur une sous-classe Animal ou l'une de ses sous-classes recevra une erreur d'assertion en les renvoyant à la documentation. Animal définit également un initialiseur privé sur une catégorie privée. La catégorie privée resterait avec votre code et les sous-classes d'Animal appelleraient cet initialiseur privé quand ils appellent super. Son but est d'appeler init sur la super-classe d'Animal (NSObject dans ce cas) et de faire toute initialisation générique qui pourrait être nécessaire. Enfin, la première ligne de la méthode initAnimal d'Animal est une affirmation que le récepteur est en fait un animal et non une sous-classe. Si le récepteur n'est pas un animal, le programme échouera avec une erreur d'assertion et le programmeur sera référé à la documentation.

Ce ne sont que deux exemples de la façon dont vous pourriez concevoir quelque chose avec vos besoins spécifiques. Cependant, je voudrais fortement vous suggérer de considérer vos contraintes de conception et voir si vous avez vraiment besoin de ce type de design car il est non standard dans Cocoa et dans la plupart des cadres de conception OO. Par exemple, vous pouvez envisager de créer des objets au niveau de la racine de divers animaux et simplement avoir un protocole Animal, exigeant que tous les différents "animaux" répondent à certains messages animaux-génériques. De cette façon, chaque animal (et les vraies sous-classes d'Animal) peuvent eux-mêmes gérer leurs initialiseurs désignés et ne pas avoir à se fier à des superclasses se comportant de manière si spécifique et non standard.

+0

Très gentil, merci pour votre temps . – zoul

Questions connexes