在 Swift 中委托可失败初始化器的正确方法是什么?

Tom*_*ing 3 swift

假设我有这门课:

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?
  }
}
Run Code Online (Sandbox Code Playgroud)

init需要一个字符串的 中,我将委托回需要一个 的那个Int。我如何知道它是否成功以便我可以继续初始化?这里正确的语法/常见模式是什么?

Swe*_*per 5

您不需要检查委托初始化程序是否成功。如果委托初始化程序失败,则整个初始化过程都会失败。

这在这段代码中很明显:

class EvenNumber {
  var num: Int
  var stringValue: String

  init?(n: Int) {
    guard n % 2 == 0 else { return nil }
    self.num = n
    stringValue = "" // you forgot to initialise stringValue in both of your initialisers
  }

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

EvenNumber(str: "5")
Run Code Online (Sandbox Code Playgroud)

“hello”不会被打印,这意味着如果失败,其余部分init(str:)不会被执行init(n:)

这是一些支持文档(您需要向下滚动一点,在“初始化失败的传播”下):

在任何一种情况下,如果您委托给另一个导致初始化失败的初始化程序,则整个初始化过程会立即失败,并且不会执行进一步的初始化代码。


(为了完整起见,包含此部分。)

现在您(或将来遇到这个问题的任何人)可能会问,“但是如果委托初始化程序失败,我想做其他事情怎么办?” 在这种情况下,您必须检查导致初始化程序失败的条件:

if n % 2 == 1 {
    self.init(n: n)
} else {
    // do something else
}
Run Code Online (Sandbox Code Playgroud)

为什么这么“笨拙”?好吧,假设你可以这样做(警告:虚构的语法):

if self.init(n: n) {
    // success!
} else {
    // fail
}
Run Code Online (Sandbox Code Playgroud)

要到达“失败”分支,我们必须已经运行了self.init(n:)self.init(n:)在失败之前,可能已经初始化了一些let属性。回想一下,let属性只能初始化一次。所以现在self.init(n: n)已经执行了,但是编译器不知道哪些let属性已经被初始化了。看到问题了吗?编译器如何验证您是否已在“失败”分支中将每个属性初始化一次?