如何保护可能失败的 属性 初始化

How to guard initialization of property that may fail

我的 class 有一个 NSURL 类型的 属性 是从字符串初始化的。该字符串在编译时已知。

要使 class 正常运行,它 必须 在初始化时 设置为预期值 不晚),所以没有必要将它定义为可选的(隐式解包或其他方式):

class TestClass: NSObject {

    private let myURL:NSURL

    ...

假设 NSURL(string:)(其中 returns NSURL?)如果传递一个在编译时已知的有效 URL 字符串将永远不会失败,我可以做类似的事情这个:

    override init() {

        myURL = NSURL(string: "http://www.google.com")!

        super.init()
    }

但是,我不知何故 对强制展开 感到不舒服,并且想以某种方式保护 URL 初始化。如果我试试这个:

    guard myURL = NSURL(string: "http://www.google.com") else {
        fatalError()
    }

Value of optional type 'NSURL?' not unwrapped; did you mean to use '!' or '?'?

(注意:没有办法在上面的代码的任何地方添加 !? 来修复错误。有条件的展开只发生在 guard let... guard var... , 并且 myURL 已经定义)

我明白为什么会失败:即使成功调用 NSURL(string:) 也会在可选的 [=17= 中返回(有效的)NSURL wrapped ], 所以我仍然需要在分配给 myURL 之前以某种方式打开它(这是非可选的,因此不兼容分配 as-is)。

我可以通过使用一个中间变量来解决这个问题:

    guard let theURL = NSURL(string: "http://www.google.com") else {
        fatalError()
    }

    myURL = theURL

...但这显然一点也不优雅。

我该怎么办?

Update 另一种不使用 guard 的方法是使用 switch,作为选项映射到 Optional枚举:

init?() {
    switch URL(string: "http://www.google.com") {
    case .none:
        myURL = NSURL()
        return nil
    case let .some(url):
        myURL = url
    }
}

尽管您仍然会得到一个 url 局部变量。


原回答

您可以将初始化器声明为可失败初始化器,并且 return nil 以防 url 字符串解析失败,而不是抛出致命错误。这将使 class 的客户更清楚初始化程序可能在某个时候失败。不过,您仍然无法摆脱 guard

init?() {

    guard let url = URL(string: "http:www.google.com") else {
        // need to set a dummy value due to a limitation of the Swift compiler
        myURL = URL()
        return nil
    }
    myURL = url

}

这在调用方增加了一点复杂性,因为它需要检查对象创建是否成功,但这是推荐的模式,以防对象初始化程序无法构造对象。您还需要放弃 NSObject 继承,因为您无法使用失败版本 (init?) 覆盖 init

您可以在 Swift blog, Apple's documentation, or this SO question.

上找到有关可失败初始化器的更多详细信息