Ios 如果显式指定类型时让行为怪异

Ios 如果显式指定类型时让行为怪异,ios,swift,syntax,optional,Ios,Swift,Syntax,Optional,假设我们有: 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) }

假设我们有:

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在第一种情况下进行展开,而在第二种情况下进行赋值

这个语法是否太接近于造成混乱?我的意思是,是的,编译器警告您在使用
而不是
时使用的是可选类型,但仍然是

更新:

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

if let not_unwrapped_a:Int???? = a {
    println(not_unwrapped_a)
}

a
将被静默地包装在
Int???
中。因此它将是一种类型的
Int????
(五)-因为
a
已经是可选的。然后它将被展开一次。

可选绑定的目的是检查可选项是否为not nil,展开并分配给包含在可选项中类型的非可选项。因此,在我看来,第一种情况是使用它的正确方式-我将使用的唯一变体是添加可选强制转换(例如,当可选的包含
AnyObject
时很有用)

我不会使用第二种情况,更喜欢可选的演员阵容:

if let unwrapped_a = a as? Int { ... }
除非如@drewag在注释中所述,显式指定了该类型以避免不清楚时出现歧义


在我看来,第三种情况应该会产生编译错误。我看不到使用可选绑定将一个可选项分配给另一个可选项。可选绑定不是一个通用的内联赋值(例如,如果让x=5{…}
),那么从概念上讲,如果左侧是可选的,它就不应该起作用-应该像右侧不是可选的一样处理它(相反,编译失败)。唯一的区别是,您让Swift在选项1中推断
unwrapped_a
的类型,而在选项2中手动给出类型。您需要执行选项2的主要原因是源值不明确——例如,如果它是一个可能返回多个类型的重载函数

案例3非常有趣

无论何时,只要有一个值,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))
在最后一个例子中,您告诉Swift您希望
而不是
成为
Int?
。但它是
let
的一部分,它要求
a
在分配给它之前先展开

鉴于此,Swift使其工作的唯一方法是隐式地将
a
包装在另一个可选文件中,这就是它所做的。现在它是一个包含一个包含nil的可选项。这不是一个值为零的可选项-这是一个包含值(包含零的可选项)的可选项。展开后,您可以选择包含nil。好像什么都没发生。但它确实如此——它被第二次包装,然后被拆开一次

如果使用
swiftc-dump ast source.swift编译示例代码,您可以看到这一点。您将看到短语
inject\u-into\u可选隐式type='Int'
Int???
是一个包含可选项的可选项

包含Optionals的Optionals不是模糊的边缘情况-它们很容易发生。例如,如果您曾经在包含optionals的数组中输入…或使用下标从包含optionals的字典中获取值,则optionals的optionals已参与该过程

考虑这一点的另一种方式是,如果您将
if let x=y{}
视为一种类似于函数的函数,
if_let
,定义如下:

func if_let<T>(optVal: T?, block: T->()) {
    if optVal != nil {
        block(optVal!)
    }
}
func if_let(optVal:T?,block:T->()){
如果optVal!=nil{
块(optVal!)
}
}
现在想象一下,如果您提供的
接受
Int?
,也就是说,
T
将是
Int?
。所以
T?
将是
Int???
。当您将一个常规的
Int?
传递到
if_let
以及该块时,Swift会将其隐式升级到
Int???
,以使其可编译。这基本上就是
如果不打开包装的话所发生的事情。\u a:Int?

我同意,隐式可选升级有时会令人惊讶(更令人惊讶的是,Swift将升级返回可选的函数,即,如果函数采用
(Int)->Int?
,它将升级
(Int)->Int
,以返回可选的函数)。但这种感觉大概是因为在这种情况下,为了方便起见,潜在的困惑是值得的


*唯一的一种

如果您认为有助于提高可读性的类型非常重要,那么使用第二种情况是完全有效和合理的。有时,在查看本地化上下文时,不清楚变量是什么类型,一些额外的注释(即使不必要)也有利于可读性。将其作为可选的强制转换将意味着a的类型尚未为
Int
。这会使代码更加混乱。@drewag:我同意这会使代码不那么模棱两可,是的,强制转换通常被解释为“它可以是另一种类型”。尽管如此,这只是我个人的喜好,我不会使用它,因为我认为周围的代码应该解决这个歧义。然而,我正在更新答案,你说的很重要。在这种情况下,这很混乱,甚至可能没有帮助,但我认为这不应该是一个编译器错误,Swift只是一直将其隐式升级逻辑应用于optionals的值。特种套管掘进机