且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Swift 3不正确的字符串插值,带有隐式展开的Optionals

更新时间:2023-02-26 19:50:47

根据

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属性,但它允许的是在需要String(其未包装类型)的上下文中对str值的隐式展开:

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? 类型.

If the expression can be explicitly type checked with a strong optional type, it will be. However, the type checker will fall back to forcing the optional if necessary. The effect of this behavior is that the result of any expression that refers to a value declared as T! will either have type T or type T?.

关于字符串插值,在后台,编译器使用

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)")

如果strnil,则两者都会崩溃.

both of which, of course, will crash if str is nil.