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

Dav*_*her 3 initialization optional swift

Swift文档初始化:Failable Initializers详细说明了如何使用它init?来创建一个可用的初始化程序,它是一个初始化程序,返回它初始化的类型的可选项.与所有期权一样,它可以是nil或不是nil.

文档还提到你可以init!用来创建一个可用的初始化程序,它返回一个隐式解包的初始化类型的可选项(参见init!Failable Initializer部分).这解开了可选项并表示它"应该"为非nil.如果是,nil并且您在没有检查的情况下访问它,程序员可能会跳过它,因为它被标记为"应该是非nil",将生成运行时错误.

两个问题:

  1. 何时/为什么要使用init!而不是init?在实现可用的初始化程序时?
  2. 由于init!总是返回一个隐式解包的可选项,"应该"为非nil,为什么不用它init代替init!

Rob*_*ier 6

在绝大多数情况下,你应该使用init而不是init!.有几种情况init!是必要的.在我的经验中最常见的是当"必须成功"初始化程序想要调用可用的初始化程序时.考虑这种情况:

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

我们知道这init()会成功,因为我们传递的是非空字符串.但是没有办法用标准的初始化程序表达它(并且编译器无论如何也无法证明它是真的).相反,我们需要使用init!:

init!() {
    self.init(something: "unknown")
}
Run Code Online (Sandbox Code Playgroud)

现在,调用者可以将结果看作是非可选的(实际上是它),即使根据它可能失败的类型.这将是一个编程错误,你会崩溃.在!这里基本上说"是的,我知道这可能会失败,但我保证它永远不会." 在斯威夫特,"承诺"的意思是"或者请崩溃".