如何保护可能失败的 属性 初始化
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.
上找到有关可失败初始化器的更多详细信息
我的 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.
上找到有关可失败初始化器的更多详细信息