为什么在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改变这种行为?
答案 0 :(得分:69)
根据SE-0054,ImplicitlyUnwrappedOptional<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
,将会崩溃。