2 votes

Quelle est la bonne façon de déléguer les initialiseurs pouvant échouer en Swift?

Supposons que j'aie cette classe :

class EvenNumber {
  var num: Int
  var stringValue: String

  init?(n: Int) {
    guard n % 2 == 0 else { return nil }
    self.num = n
  }

  init?(str: String) {
    guard let n = Int(str) else { return nil }
    self.init(n: n)
    //set stringValue?
  }
}

Dans l'init qui prend une chaîne de caractères, je délègue à celui qui prend un Int. Comment puis-je savoir si cela a réussi pour pouvoir continuer l'initialisation ? Quelle est la syntaxe appropriée / le motif commun ici ?

4voto

Sweeper Points 1267

Vous n'avez pas besoin de vérifier que l'initialiseur délégué a réussi. Si l'initialiseur délégué a échoué, tout le processus d'initialisation échoue.

Cela est évident dans ce code:

class EvenNumber {
  var num: Int
  var stringValue: String

  init?(n: Int) {
    guard n % 2 == 0 else { return nil }
    self.num = n
    stringValue = "" // vous avez oublié d'initialiser stringValue dans vos deux initialiseurs
  }

    // vous avez oublié "convenience"
  convenience init?(str: String) {
    guard let n = Int(str) else { return nil }
    self.init(n: n)
    print("hello")
    stringValue = ""
  }
}

EvenNumber(str: "5")

"hello" n'est pas imprimé, ce qui signifie que le reste de l'initialisation ne s'exécute pas si init(n:) échoue.

Voici une documentation en soutien (vous devez faire défiler un peu, sous "Propagation de l'échec d'initialisation"):

Dans les deux cas, si vous déléguez à un autre initialiseur causant un échec d'initialisation, l'ensemble du processus d'initialisation échoue immédiatement et aucun autre code d'initialisation n'est exécuté.


(Cette section est incluse pour plus de clarté.)

Maintenant, vous (ou quiconque consultera cette question à l'avenir) pourriez demander : "mais que se passe-t-il si je veux faire quelque chose d'autre si l'initialiseur délégué échoue?" Dans ce cas, vous devez vérifier la condition qui provoque l'échec de l'initialiseur:

if n % 2 == 1 {
    self.init(n: n)
} else {
    // faire autre chose
}

Pourquoi est-ce si "maladroit"? Eh bien, disons que vous pouviez faire ceci (attention : syntaxe inventée):

if self.init(n: n) {
    // succès!
} else {
    // échec
}

Pour arriver à la branche "échec", nous devons avoir déjà exécuté self.init(n:). self.init(n:), avant d'échouer, pourrait déjà avoir initialisé certaines propriétés let. Rappelez-vous que les propriétés let ne peuvent être initialisées qu'une fois. Donc maintenant self.init(n: n) a été exécuté, mais le compilateur ne sait pas quelles propriétés let ont été initialisées. Voyez-vous le problème? Comment le compilateur va-t-il vérifier que vous avez initialisé chaque propriété exactement une fois dans la branche "échec"?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X