问题描述
为什么在 Swift 3 中使用字符串插值时隐式解包的选项没有解包?
Why are implicitly unwrapped optionals not unwrapped when using string interpolation in Swift 3?
示例:在操场上运行以下代码
Example:Running the following code in the playground
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")
当然我可以用 +
运算符连接字符串,但我在我的应用程序中几乎到处都使用字符串插值,但由于这个(错误?),现在不再工作了.
Of course I can concatenate strings with the +
operator but I'm using string interpolation pretty much everywhere in my app which now doesn't work anymore due to this (bug?).
这到底是一个错误还是他们有意在 Swift 3 中改变了这种行为?
Is this even a bug or did they intentionally change this behaviour with Swift 3?
推荐答案
根据 SE-0054,ImplicitlyUnwrappedOptional
不再是一个独特的类型;现在只有 Optional
.
As per SE-0054, ImplicitlyUnwrappedOptional<T>
is no longer a distinct type; there is only Optional<T>
now.
声明仍然允许被注释为隐式解包选项T!
,但这样做只是添加了一个隐藏属性来通知编译器它们的值可能会在需要解包类型的上下文中被强制解包T
;它们的实际类型现在是 T?
.
Declarations are still allowed to be annotated as implicitly unwrapped optionals T!
, but doing so just adds a hidden attribute to inform the compiler that their value may be force unwrapped in contexts that demand their unwrapped type T
; their actual type is now T?
.
所以你可以想到这个声明:
So you can think of this declaration:
var str: String!
实际上看起来像这样:
@_implicitlyUnwrapped // this attribute name is fictitious
var str: String?
只有编译器可以看到这个 @_implicitlyUnwrapped
属性,但它允许在需要 Stringstr
的值/code>(它的解包类型):
Only the compiler sees this @_implicitlyUnwrapped
attribute, but what it allows for is the implicit unwrapping of str
's value in contexts that demand a String
(its unwrapped type):
// `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
可以作为强可选进行类型检查,它将是:
But in all other cases where str
can be type-checked as a strong optional, it will be:
// `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.
并且编译器总是更喜欢这样对待它而不是强制解包.
And the compiler will always prefer treating it as such over force unwrapping.
正如提案所说(强调我的):
As the proposal says (emphasis mine):
如果表达式可以使用强可选类型进行显式类型检查,则为.但是,如果需要,类型检查器将回退到强制可选.这种行为的结果是引用声明为T!
的值的任何表达式的结果要么具有类型T
,要么类型T?
.
当谈到字符串插值时,编译器在底层使用了来自 _ExpressibleByStringInterpolation
协议以评估字符串插值段:
When it comes to string interpolation, under the hood the compiler uses this initialiser from the _ExpressibleByStringInterpolation
protocol in order to evaluate a string interpolation segment:
/// 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)
因此,当您的代码隐式调用时:
Therefore when implicitly called by your code:
var str: String!
str = "Hello"
print("The following should not be printed as an optional: (str)")
由于 str
的实际类型是 String?
,默认情况下,编译器会推断通用占位符 T
是什么.因此 str
的值不会被强制解包,你最终会看到一个可选的描述.
As str
's actual type is String?
, by default that's what the compiler will infer the generic placeholder T
to be. Therefore the value of str
won't be force unwrapped, and you'll end up seeing the description for an optional.
如果您希望在字符串插值中使用 IUO 时强制展开,您可以简单地使用强制展开运算符 !
:
If you wish for an IUO to be force unwrapped when used in string interpolation, you can simply use the force unwrap operator !
:
var str: String!
str = "Hello"
print("The following should not be printed as an optional: (str!)")
或者您可以强制使用其非可选类型(在本例中为 String
),以强制编译器为您隐式强制解包:
or you can coerce to its non-optional type (in this case String
) in order to force the compiler to implicitly force unwrap it for you:
print("The following should not be printed as an optional: (str as String)")
当然,如果 str
为 nil
,两者都会崩溃.
both of which, of course, will crash if str
is nil
.
这篇关于Swift 3 不正确的字符串插值,隐式解包 Optionals的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!