Swift 3错误的字符串插值与隐式解包的Optionals

时间:2016-09-16 17:32:01

标签: swift string swift3 string-interpolation

为什么在Swift 3中使用字符串插值时,隐式解包的选项未解包?

示例: 在操场上运行以下代码

var str: String!
str = "Hello"

print("The following should not be printed as an optional: \(str)")

生成此输出:

The following should not be printed as an optional: Optional("Hello")

当然我可以使用+运算符连接字符串,但我在我的应用程序中几乎无处不在使用字符串插值,现在由于这个原因而无法工作(错误?)。

这甚至是一个错误,还是他们故意用Swift 3改变这种行为?

1 个答案:

答案 0 :(得分:69)

根据SE-0054ImplicitlyUnwrappedOptional<T>不再是一个独特的类型;现在只有Optional<T>

仍然允许将声明注释为隐式解包的选项T!,但这样做只会添加一个隐藏属性,以通知编译器,它们的值可能会在需要解包的类型{{1}的上下文中被强行解包。 };他们的实际类型现在是T

所以你可以想到这个宣言:

T?

实际上看起来像这样:

var str: String!

只有编译器才会看到此@_implicitlyUnwrapped // this attribute name is fictitious var str: String? 属性,但它允许的是在需要@_implicitlyUnwrapped(其未打包类型)的上下文中隐式展开str的值):

String

但在// `str` cannot be type-checked as a strong optional, so the compiler will // implicitly force unwrap it (causing a crash in this case) let x: String = str // We're accessing a member on the unwrapped type of `str`, so it'll also be // implicitly force unwrapped here print(str.count) 可以作为强选项进行类型检查的所有其他情况下,它将是:

str

并且编译器总是更愿意将其视为强制解包。

正如提案所说(强调我的):

  

如果可以使用强可选类型显式地选中表达式,则它将是。但是,如果需要,类型检查器将回退到强制可选。此行为的影响是引用声明为// `x` is inferred to be a `String?` (because we really are assigning a `String?`) let x = str let y: Any = str // `str` is implicitly coerced from `String?` to `Any` print(str) // Same as the previous example, as `print` takes an `Any` parameter. 的值的任何表达式的结果将具有类型T!或类型T

说到字符串插值,编译器使用_ExpressibleByStringInterpolation protocol中的初始化来评估字符串插值段:

T?

因此,当您的代码隐式调用时:

/// Creates an instance containing the appropriate representation for the
/// given value.
///
/// Do not call this initializer directly. It is used by the compiler for
/// each string interpolation segment when you use string interpolation. For
/// example:
///
///     let s = "\(5) x \(2) = \(5 * 2)"
///     print(s)
///     // Prints "5 x 2 = 10"
///
/// This initializer is called five times when processing the string literal
/// in the example above; once each for the following: the integer `5`, the
/// string `" x "`, the integer `2`, the string `" = "`, and the result of
/// the expression `5 * 2`.
///
/// - Parameter expr: The expression to represent.
init<T>(stringInterpolationSegment expr: T)

由于var str: String! str = "Hello" print("The following should not be printed as an optional: \(str)") 的实际类型为str,默认情况下,编译器将推断通用占位符String?的内容。因此,T的值不会被强制解包,您最终会看到可选的描述。

如果您希望在字符串插值中使用IUO强制解包,您只需使用强制解包操作符str

!

或者您可以强制使用其非可选类型(在本例中为var str: String! str = "Hello" print("The following should not be printed as an optional: \(str!)") ),以强制编译器隐式强制为您解包:

String
当然,如果print("The following should not be printed as an optional: \(str as String)") str

将会崩溃。