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

3

假设我有这个类:

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?
  }
}

在接收一个字符串的init方法中,我会委托给接收Int的方法。那么我如何知道它是否成功并继续初始化呢?这里的正确语法和常见模式是什么?
1个回答

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")

"

"hello"不会被打印出来,这意味着如果init(n:)失败,则init(str:)的其余部分不会被执行。

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

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

"
(为了完整起见,包括此部分。)
现在你(或者将来看到这个问题的人)可能会问:“但是如果我希望在委托初始化程序失败时执行其他操作怎么办?” 在这种情况下,您必须检查导致初始化程序失败的条件:
if n % 2 == 1 {
    self.init(n: n)
} else {
    // do something else
}

为什么这个代码看起来有点“笨拙”呢?那是因为,我们假设你可以这样写(警告:这只是一种虚构的语法):

if self.init(n: n) {
    // success!
} else {
    // fail
}

要进入“失败”分支,我们必须已经运行了self.init(n:)。在它失败之前,self.init(n:)可能已经初始化了一些let属性。请记住,let属性只能初始化一次。因此,现在执行了self.init(n: n),但编译器不知道哪些let属性已经被初始化。看到问题了吗?编译器如何验证您在“失败”分支中每个属性都恰好初始化了一次?

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接