swift 具有 throw init 行为的惰性变量
swift lazy var with throw init behavior
我不确定这是一个错误还是它真的应该如何工作?
class A {
init() throws { }
}
class B {
lazy var instance = A()
}
此代码使用 XCode 9 和最新的 Swift
版本编译没有错误,并且工作完美,除非 Class A
init()
真的抛出,然后 lazy var 是空指针。但是不应该以某种方式不编译这段代码吗?
作为对您问题的回答:
But shouldn't be this code somehow not be compiled?
好吧,在某些时候你的代码片段没有任何问题(因为 - 正如你提到的 - class A
init 实际上并没有抛出),所以它可能是 编译没有任何问题。为了更清楚,将其视为与以下情况类似的情况:
let myString: String? = nil
print(myString!) // crashes!
它会被编译得很好!虽然我们都知道它在评估 myString!
时崩溃,但我们确实知道它会导致 运行-time 崩溃,但这并不意味着编译器应该阻止它,因为它在某些时候可能是有效的(例如,如果我们将其声明为 let myString: String? = "Hello"
);与您的情况类似,它在某些时候可能是有效的-如上所述-。
通常,对于这种情况,我们作为开发人员有责任根据所需的行为来处理它。
针对这个案例,我们可能要问:
“我们如何实现 instance
惰性变量来捕获错误(使用 do-catch
块)?”
实际上,这段代码不会编译:
class B {
lazy var instance:A = {
do {
let myA = try A()
return myA
} catch {
print(error)
}
}()
}
抱怨:
Missing return in a closure expected to return 'A'
因为显然到达catch块意味着没有任何东西可以返回。另外,正如您提到的,即使您将其实现为
lazy var instance = A()
您不会得到编译时错误,但是尝试将它用于实际抛出应该会导致 运行 时间错误:
let myB = B()
print(myB.instance) // crash!
我建议解决此问题的方法是将 instance
声明为惰性 可选 变量:
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)
应该记录:
caught error
nil
不会造成任何崩溃。否则,它应该可以正常工作,例如:
let myB = B()
myB.instance?.doSomething() // works fine
这确实是一个错误 (SR-7862) – 你不能从 属性 初始化上下文中抛出错误(即使你可以,你也需要在调用前加上前缀 try
), 因此编译器应该产生一个错误。
我已经打开了一个拉取请求来解决这个问题 (#17022)。
编辑: 该补丁现已被精心挑选到 4.2 分支,因此它将在 Swift 4.2 和 [=22] 的发布中得到修复=] 10(在发布之前你可以 try a 4.2 snapshot)。
我不确定这是一个错误还是它真的应该如何工作?
class A {
init() throws { }
}
class B {
lazy var instance = A()
}
此代码使用 XCode 9 和最新的 Swift
版本编译没有错误,并且工作完美,除非 Class A
init()
真的抛出,然后 lazy var 是空指针。但是不应该以某种方式不编译这段代码吗?
作为对您问题的回答:
But shouldn't be this code somehow not be compiled?
好吧,在某些时候你的代码片段没有任何问题(因为 - 正如你提到的 - class A
init 实际上并没有抛出),所以它可能是 编译没有任何问题。为了更清楚,将其视为与以下情况类似的情况:
let myString: String? = nil
print(myString!) // crashes!
它会被编译得很好!虽然我们都知道它在评估 myString!
时崩溃,但我们确实知道它会导致 运行-time 崩溃,但这并不意味着编译器应该阻止它,因为它在某些时候可能是有效的(例如,如果我们将其声明为 let myString: String? = "Hello"
);与您的情况类似,它在某些时候可能是有效的-如上所述-。
通常,对于这种情况,我们作为开发人员有责任根据所需的行为来处理它。
针对这个案例,我们可能要问:
“我们如何实现 instance
惰性变量来捕获错误(使用 do-catch
块)?”
实际上,这段代码不会编译:
class B {
lazy var instance:A = {
do {
let myA = try A()
return myA
} catch {
print(error)
}
}()
}
抱怨:
Missing return in a closure expected to return 'A'
因为显然到达catch块意味着没有任何东西可以返回。另外,正如您提到的,即使您将其实现为
lazy var instance = A()
您不会得到编译时错误,但是尝试将它用于实际抛出应该会导致 运行 时间错误:
let myB = B()
print(myB.instance) // crash!
我建议解决此问题的方法是将 instance
声明为惰性 可选 变量:
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)
应该记录:
caught error
nil
不会造成任何崩溃。否则,它应该可以正常工作,例如:
let myB = B()
myB.instance?.doSomething() // works fine
这确实是一个错误 (SR-7862) – 你不能从 属性 初始化上下文中抛出错误(即使你可以,你也需要在调用前加上前缀 try
), 因此编译器应该产生一个错误。
我已经打开了一个拉取请求来解决这个问题 (#17022)。
编辑: 该补丁现已被精心挑选到 4.2 分支,因此它将在 Swift 4.2 和 [=22] 的发布中得到修复=] 10(在发布之前你可以 try a 4.2 snapshot)。