在 Swift 中实现 Failable Initializer 时,为什么要使用 `init!` 而不是 `init?`?

Why would you use `init!` instead of `init?` when implementing a Failable Initializer in Swift?

Swift 文档 Initialization: Failable Initializers 详细说明了如何使用 init? 创建可失败初始化程序,这是一个 returns 和 可选的初始化程序 它初始化的类型。对于所有可选值,它可以是 nil 或非 nil.

文档还提到你可以使用 init! 创建一个可失败的初始化器,returns 一个 隐式解包的可选 它初始化的类型(见init!Failable Initializer 部分)。这解开可选的包装并表明它 "should" 是非 nil。如果它是 nil 并且您未经检查就访问它,程序员可能会跳过它,因为它被标记为 "should be non-nil",将生成运行时错误。

两个问题:

  1. When/Why 在实现可失败初始化器时,你会使用 init! 而不是 init? 吗?
  2. 因为 init! 总是 returns 一个隐式解包的可选值 "should" 是非 nil,你为什么不直接使用 init 而不是init!?

在绝大多数情况下,您应该使用 init 而不是 init!。在少数情况下 init! 是必要的。根据我的经验,最常见的情况是 "must succeed" 初始化程序想要调用可失败的初始化程序。考虑这种情况:

struct NotEmpty {
    let something: String
    init?(something: String) {
        guard !something.isEmpty else { return nil }
        self.something = something
    }

    init() {
        self.init(something: "unknown")! // <-- This is illegal
    }
}

我们知道 init() 会成功,因为我们传递的是非空字符串。但是没有办法用标准的初始值设定项来表达它(而且编译器也无法证明它是真的)。相反,我们需要使用 init!:

init!() {
    self.init(something: "unknown")
}

现在调用者可以将结果视为非可选的(实际上是非可选的),即使根据类型它可能会失败。那将是一个编程错误,你会崩溃。这里的 ! 基本上表示 "yeah, I know it could fail, but I promise it never will." 而在 Swift 中, "promise" 表示 "or else please crash."

您可以在 类 中使用它来覆盖可失败的初始化程序并使其成为不可失败的。

在处理隐式解包时必须小心谨慎:它可能很危险,因为它有可能导致应用程序崩溃。

你可以从 init 委托?初始化!反之亦然,你可以覆盖 init?与初始化!反之亦然。

这意味着您可以将一些 init? 转换为 init! 如果您非常确定它不会失败,反之亦然。在文档中有以下示例:

enum TemperatureUnit {
    case Kelvin, Celsius, Fahrenheit
        init?(symbol: Character) {
        switch symbol {
        case "K":
            self = .Kelvin
        case "C":
            self = .Celsius
        case "F":
            self = .Fahrenheit
        default:
            return nil
        }
    }
}

您可能有一些类似的代码,并且您可以仅使用常量对其进行初始化,而不使用用户输入或其他一些输入源。所以你很确定它不会失败,为什么不让它 init! ?