提问人:Ilea Cristian 提问时间:12/20/2014 最后编辑:Ilea Cristian 更新时间:12/20/2014 访问量:344
如果让在显式指定类型时表现得很奇怪
if let acting weird when explicitly specifying type
问:
假设我们有:
let a:Int? = nil
// block not executed - unwapping done, type is inferred
if let unwrapped_a = a {
println(unwrapped_a)
}
// block not executed - unwrapping done, type is specified
if let unwrapped_a:Int = a {
println(unwrapped_a)
}
// block gets executed - unwrapping not done, new local constant + assignment is done instead?
if let not_unwrapped_a:Int? = a {
println(not_unwrapped_a)
}
那么,我是否应该假设 Swift 在第一种情况下进行解包,但在第二种情况下进行赋值?
这种语法是不是有点太接近了,不会造成混淆?我的意思是,是的,编译器警告您在使用 时使用的是可选类型,但仍然如此。not_unwrapped_a
更新:
因此,在 Airspeed Velocity 的回答之后,我发现了另一个(但实际上相同)奇怪的案例:
if let not_unwrapped_a:Int???? = a {
println(not_unwrapped_a)
}
a
将默默地包裹在.所以它将是一种 (五) - 因为已经是可选的。然后它会被解开一次。Int????
Int?????
a
答:
可选绑定的目的是检查一个可选的不为零,解开并分配给非可选的类型,该类型包含在可选中。所以在我看来,第一种情况是正确的使用方式 - 我唯一会使用的变体是添加一个可选的强制转换(例如,当可选包含时很有用)。AnyObject
我不会使用第二种情况,更喜欢可选的演员:
if let unwrapped_a = a as? Int { ... }
除非,正如 @drewag 在注释中指出的那样,明确指定类型以避免在不清楚时产生歧义。
在我看来,第三种情况应该会产生编译错误。我没有看到任何使用可选绑定将一个可选分配给另一个可选。可选绑定不是通用的内联赋值(例如 ),因此从概念上讲,如果左侧是可选的,则它不应该起作用 - 它的处理方式应该与右侧不是可选的(编译失败)相同。if let x = 5 {...}
评论
Int
案例 1 和案例 2 是相同的——它们都是对新变量的内容赋值。唯一的区别是,你让 Swift 推断选项 1 中的类型,而你手动给出选项 2 中的类型。您需要执行选项 2 的主要原因是源值是否不明确 - 例如,如果它是一个可以返回多个类型的重载函数。a
unwrapped_a
案例 3 非常有趣。
每当你有一个值时,Swift 总是愿意默默地将它升级为一个可选的包装值,如果它有助于使类型匹配和代码编译。类型的快速自动升级是相当罕见的(例如,它不会隐式地将 an 升级为 an),但将值升级为 optionals 是一个例外。Int16
Int32
这意味着您可以在任何需要可选值的地方传递值,而不必费心包装它:
func f(maybe: Int?) { ... }
let i = 1
// you can just pass a straight value:
f(i)
// Swift will turn this into this:
f(Optional(i))
所以在你的最后一个例子中,你已经告诉 Swift 你想成为一个 .但它是 a 的一部分,在分配给它之前需要解开它。not_unwrapped_a
Int?
let
a
有了这个,Swift 让它工作的唯一方法就是隐式包装另一个可选,所以这就是它的作用。现在它是一个 optional 包含一个 optional containing nil。这不是一个零值的可选 - 即一个包含值的可选(一个包含零的可选)。展开后,可为您提供一个可选的包含 nil。好像什么都没发生。但它确实做到了——它被包裹了第二次,然后被解开了一次。a
如果使用 编译示例代码,则可以看到这一点。你会看到短语 .是可选的,包含可选的。swiftc -dump-ast source.swift
inject_into_optional implicit type='Int??’
Int??
包含可选的可选不是晦涩难懂的边缘情况 - 它们很容易发生。例如,如果你曾经...在包含可选项的数组中,或者使用下标从包含可选项的字典中获取值,则该过程中涉及可选项的可选项。
另一种思考方式是,如果你把 看作是一个函数,定义如下:if let x = y { }
if_let
func if_let<T>(optVal: T?, block: T->()) {
if optVal != nil {
block(optVal!)
}
}
现在想象一下,如果你提供一个需要 - 也就是说,将是一个 .所以会是一个.当你将一个常规代码与该块一起传递时,Swift 会隐式地将其升级为 an 以使其编译。这基本上就是 .block
Int?
T
Int?
T?
Int??
Int?
if_let
Int??
if let not_unwrapped_a:Int?
我同意,隐含的可选升级有时会令人惊讶(更令人惊讶的是,Swift 将升级返回可选的函数,即如果一个函数需要 ,它将升级 an 以返回可选)。但据推测,在这种情况下,为了方便起见,潜在的混淆是值得的。(Int)->Int?
(Int)->Int
* 只有一种
评论
if let
if
if let
nil