swift lazy var with throw init behavior

时间:2018-06-04 07:48:24

标签: swift throw lazy-initialization

我不确定这是不是一个错误,或者它确实是如何运作的?

class A {
    init() throws { }
}

class B {
    lazy var instance = A()
}

此代码使用XCode 9和最新Swift版本编译时没有错误,并且除非Class A init()真正抛出,否则工作完美,然后lazy var为空指针。但不应该以某种方式编译这段代码吗?

2 个答案:

答案 0 :(得分:5)

这确实是一个错误(SR-7862) - 你不能从属性初始化上下文中抛出错误(即使你可以,你也需要使用$checkboxesFound == 0为调用添加前缀),因此编译器应该产生错误。

我已经打开了一个拉取请求来修复此问题(#17022)。

编辑:该修补程序现已被挑选到4.2分支,所以它将被修复为使用Xcode 10发布Swift 4.2(直到发布时你可以{{3 }})。

答案 1 :(得分:1)

作为对你问题的回答:

  

但不应该以某种方式编译这段代码吗?

好吧,在某些时候你的代码片段没有任何问题(因为 - 你提到过 - 类A init实际上没有抛出),所以它可能是编译没有任何问题。为了更清楚,请将其视为与下列案例类似的案例:

let myString: String? = nil
print(myString!) // crashes!

它会被编译得很好!虽然我们都知道它在评估myString!时会崩溃,但我们知道它会导致 运行时 崩溃,但这并不意味着编译器应该阻止它,因为它在某些时候可能是有效的(例如,如果我们将其声明为let myString: String? = "Hello");与您的情况类似,它可能在某些时候有效 - 如上所述 - 。

通常,对于这种情况,我们 - 开发人员 - 负责根据所需的行为来处理它。

参考此案例,我们可能需要问:

“我们如何实现instance lazy变量来捕获错误(使用do-catch块)?”

实际上,此代码不会编译:

class B {
    lazy var instance:A = {
        do {
            let myA = try A()
            return myA
        } catch {
            print(error)
        }
    }()
}
抱怨:

  

在预期返回'A'的闭包中缺少回报

因为显然到达catch块意味着没有任何东西可以返回。另外,正如您所提到的,即使您将其实现为

lazy var instance = A()

你不会得到编译时错误,然而试图将它与实际投掷一起使用会导致运行时错误:

let myB = B()
print(myB.instance) // crash!

我建议解决此问题的方法是将instance声明为lazy 可选变量:

class B {
    lazy var instance:A? = {
        do {
            let myA = try A()
            return myA
        } catch {
            print(error)
        }

        return nil
    }()
}

此时,如果我们假设A初始值设定项始终抛出,则尝试访问它:

let myB = B()
print(myB.instance)

应记录:

  

发现错误

     

不会导致任何崩溃。否则,它应该可以正常工作,例如:

let myB = B()
myB.instance?.doSomething() // works fine