2017-05-25 1 views
2

J'ai construit ce struct pour gérer un type spécifique de données que je prévois d'utiliser dans certaines classes personnalisées.
Mon problème est que la variable featureSubSet est capable de faire partie de quelques énumérations, et quand cette structure est initialisée, elle ne sait pas quelle enum elle sera, donc je l'ai déclarée comme Any. Lorsque l'init public est appelé, il canalise de manière appropriée les données vers la méthode init privée nécessaire afin qu'elle puisse être initialisée correctement et complètement.Pourquoi l'erreur "self" est-elle utilisée avant l'appel de self.init dans ma structure?

Je reçois l'erreur à la fin de la méthode public init, mais je ne suis pas sûr de savoir comment la faire disparaître.

struct Feature { 
//MARK: Variables needed for Feature 
var featureSet: FeatureType 
var featureSubSet: Any 
var effect: String 
var active: Bool? 
var skill: Skill? 
var ability: Ability? 

public init(base: String, sub: String, effect: String, skill: Skill? = nil, ability: Ability? = nil) { 
    switch base { 
    case featureCategoryList()[0]: // Character Features 
     self.init(CharacterFeature: sub, effect: effect) 
    case featureCategoryList()[1]: // Combat Features 
     self.init(CombatFeature: sub, effect: effect) 
    case featureCategoryList()[2]: //Skill Features 
     guard let newSkill = skill else { 
      print("No skill") 
      return 
     } 
     self.init(SkillFeature: sub, effect: effect, skill: newSkill) 

    default: 
     print("Somehow you picked something not on the list.") 
     break 
    } 
} 

private init(CharacterFeature sub: String, effect: String) { 
    self.featureSet = .CharacterFeatures 
    self.featureSubSet = CharacterFeatures.init(rawValue: sub)! 
    self.effect = effect 
} 

private init(CombatFeature sub: String, effect: String) { 
    self.featureSet = .CombatFeatures 
    self.featureSubSet = CharacterFeatures.init(rawValue: sub)! 
    self.effect = effect 
} 

private init(SkillFeature sub: String, effect: String, skill: Skill) { 
    self.featureSet = .SkillFeatures 
    self.featureSubSet = CharacterFeatures.init(rawValue: sub)! 
    self.skill = skill 
    self.effect = effect 
} 

//MARK: Feature functions 
func string() -> String { 
    //TODO: Make a string output for what the feature is. 
    return "" 
} 
} 
+0

Quelle erreur obtenez-vous? –

Répondre

2

Cette approche est incorrecte. Si vous trouvez quelque chose comme Any, vous êtes certainement sur le mauvais chemin. Il est difficile d'explorer ce code car il définit tant d'autres types, mais en le regardant, on ne sait pas pourquoi vous avez besoin de Any ici. featureSubSet est toujours de type CharacterFeatures. Toutes les valeurs d'une énumération sont du même "type".

Vous avez une erreur ici:

case featureCategoryList()[2]: //Skill Features 
     guard let newSkill = skill else { 
      print("No skill") 
      return 
     } 

Et ici:

default: 
     print("Somehow you picked something not on the list.") 
     break 
    } 

Ce retour sans initialisation self (et est probablement votre problème). Si vous pouvez échouer, alors vous devez en faire un initialiseur optionnel (init?) ou un initialiseur de lancement, ou vous devez appeler fatalError() ou une méthode de plantage similaire en cas d'erreur. Vous ne pouvez pas simplement revenir sans initialiser.

Cependant, je vous recommande fortement de le reconcevoir. Je ne vois aucune raison que les paramètres devraient être des chaînes. Plutôt que de passer en "character" et de chercher dans une table, faites passer l'appelant .character ou .combat comme enum (vous avez déjà l'enum).

Notez que dans Swift 3, les cas d'énumération doivent toujours commencer par une lettre minuscule.

Il est un peu difficile de comprendre ce qui se passe dans votre code, mais cela ne ressemble pas du tout à une structure. Cela ressemble à une énumération.Quelque chose comme ceci:

enum CharacterFeature { 
    // Features for characters 
} 

enum CombatFeature { 
    // Features for combat 
} 

enum Skill { 
    // skills 
} 

enum Effect { 
    // effects 
} 

enum Feature { 
    case character(CharacterFeature, Effect) 
    case combat(CombatFeature, Effect) 
    case skill(Skill, Effect) 
} 

Alternativement, vous pouvez utiliser une struct ici, surtout s'il y a plus de choses qui sont partagées par toutes les caractéristiques:

struct Feature { 
    enum Kind { 
     case character(CharacterFeature, Effect) 
     case combat(CombatFeature, Effect) 
     case skill(Skill, Effect) 
    } 

    let kind: Kind 
    let effect: Effect 
} 

Le point clé est que vous devez généralement travailler types explicites, pas de chaînes. Si vous avez besoin de trop d'options, vous faites souvent quelque chose de mal.

+0

Bonne réponse! J'ajouterais seulement l'option d'utiliser 'String', comme enum * type brut *, s'il a besoin de garder ces chaînes (méchantes!) À d'autres fins - et il veut un certain contrôle sur les conventions de nommage spécifiées utilisées. –

+0

En fait, je suis complètement d'accord. Merci pour l'exemple. Malheureusement, cette erreur m'a rendu folle, donc je n'avais pas terminé l'instruction switch et les méthodes init privées, mais la raison pour laquelle j'ai utilisé Any comme type de données pour featureSubSet est qu'il est possible qu'il soit l'un des quatre types d'enum. En utilisant votre alternative, comment recommanderiez-vous de traiter avec l'initialisation lors de l'entrée de l'utilisateur? J'utilisais les chaînes pour le filtrer à travers la bonne séquence de méthodes init pour l'initialiser complètement, quelle serait la meilleure façon de le faire? –

+0

Généralement, la façon de gérer les entrées de l'utilisateur consiste à rendre les enum 'RawRepresentable' à' String'. Donc, vous initialisez 'Kind' en utilisant une chaîne, puis passez à' Feature'. C'est beaucoup plus contenu que de passer beaucoup de chaînes à 'Feature'. Comme vous le voyez, vous évitez le 'Any' en utilisant les données associées sur l'énumération. Si vous pouviez donner un exemple des données d'entrée et ce que vous voulez obtenir, je suis sûr que nous pouvons créer un exemple. –

0

En supposant featureCategoryList est une fonction struct Feature, votre erreur d'origine:

self used before self.init call

pourrait provenir de ces lignes:

case featureCategoryList()[0]: 
    ... 
case featureCategoryList()[1]: 
    ... 
case featureCategoryList()[2]: 

puisque vous êtes implicitement appeler self avant l'initialisation.

Après avoir corrigé l'erreur ci-dessus, vous aussi besoin de fixer ces derniers aussi bien:

self.init isn't called on all paths before returning from initializer

Découvrez la réponse de Rob pour quelques grandes alternatives.