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

时间:2014-12-19 16:05:28

标签: 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)
}

// 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已经是可选的。然后它将被解开一次。

2 个答案:

答案 0 :(得分:5)

案例1和案例2完全相同 - 它们都是a内容对新变量的赋值。唯一的区别是你要离开Swift来推断选项1中unwrapped_a的类型,而你手动给出选项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))

所以在你的最后一个例子中,你告诉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!)
    }
}

现在想象一下,如果您提供了block Int?,那么T就是Int?。因此T?将是Int??。当您将常规Int?传递给if_let以及该块时,Swift会隐式将其升级为Int??以使其编译。这基本上是if let not_unwrapped_a:Int?发生的事情。

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

*只有

答案 1 :(得分:4)

可选绑定的目的是检查一个可选的for nil,unwrap并赋值给可选中包含的类型的非可选项。因此,在我看来,第一种情况是使用它的正确方法 - 我将使用的唯一变体是添加可选的强制转换(例如,当可选项包含AnyObject时)。

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

if let unwrapped_a = a as? Int { ... }

除非@drewag在评论中指出,否则明确指定类型以避免在不清楚时出现歧义。

在我看来,第三种情况应该会产生编译错误。我没有看到任何使用可选绑定将可选项分配给另一个可选项。可选绑定不是通用的内联赋值(例如if let x = 5 {...}),因此从概念上讲,如果左侧是可选的,它应该不起作用 - 它应该以与右侧不是可选的相同的方式处理(对于编译失败了。)