如果在明确指定类型时让它表现得很奇怪

Ile*_*ian 6 syntax optional ios swift

假设我们有:

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)
}
Run Code Online (Sandbox Code Playgroud)

那么我应该假设Swift在第一种情况下进行了解包,而在第二种情况下进行了分配吗?

这种语法是不是太接近造成混乱?我的意思是,是的,编译器警告您在使用时正在使用可选类型not_unwrapped_a,但仍然如此.

更新:

所以在Airspeed Velocity的回答之后,我发现另一个(但实际上是相同的)奇怪的情况:

if let not_unwrapped_a:Int???? = a {
    println(not_unwrapped_a)
}
Run Code Online (Sandbox Code Playgroud)

a将被默默地包裹在一个Int????.所以它将是一种Int?????(五种) - 因为a它已经是可选的.然后它将被解开一次.

Air*_*ity 5

案例1和案例2是相同的 - 它们都是a新变量内容的赋值.唯一的区别是你要离开Swift推断unwrapped_a选项1中的类型,而你手动给出选项2中的类型.你需要做选项2的主要原因是源值是不明确的 - 对于例如,如果它是一个可以返回多个类型的重载函数.

案例3非常有趣.

每当你有一个值时,Swift总是愿意默默地将它升级为一个可选的包装值,如果它有助于使类型匹配和代码编译.类型的Swift自动升级是相当罕见的(例如,它不会隐式升级Int16到一个Int32),但是对optionals的值是一个例外.

这意味着您可以在需要可选项的地方传递值,而无需费心去包装它:

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))
Run Code Online (Sandbox Code Playgroud)

所以在你的最后一个例子中,你告诉Swift你想not_unwrapped_a成为一个Int?.但它let是需要a在分配给它之前解开的一部分.

提出这一点,Swift可以使其工作的唯一方法是隐式包装a另一个可选项,这就是它的作用.现在它是一个可选项,包含一个包含nil的可选项.这不是一个零值可选 - 这是一个包含值的可选项(包含nil的可选值).展开会给你一个包含nil的可选项.它看起来像什么都没有发生.但它确实 - 它被包裹了第二次,然后解开了一次.

如果使用编译示例代码,可以看到这一点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!)
    }
}
Run Code Online (Sandbox Code Playgroud)

现在想象一下,如果你提供block了一个Int?- 那T将是一个Int?.那T?将是一个Int??.当您将常规传递Int?if_let该块时,Swift会隐式将其升级为a Int??以使其编译.这基本上就是发生了什么if let not_unwrapped_a:Int?.

我同意,隐式可选升级有时会令人惊讶(更令人惊讶的是,Swift将升级返回选项的函数,即如果函数需要(Int)->Int?,它将升级(Int)->Int为返回可选的).但据推测,感觉是潜在的混乱是值得的,因为在这种情况下方便.

*只有一种