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?
SE-0054 により、ImplicitlyUnwrappedOptional<T>
は特殊タイプではなくなりました。現在はOptional<T>
のみです。
宣言には暗黙的にラップされていないオプションT!
として注釈を付けることができますが、そうすることで、隠された属性を追加して、アンラップされたタイプ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
型またはT?
型のいずれかになることです。 。
文字列補間に関しては、コンパイラは内部で _ExpressibleByStringInterpolation
protocol の初期化子を使用して、文字列補間セグメントを評価します。
/// 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
を推測します。したがって、str
の値は強制的にラップ解除されず、オプションの説明が表示されます。
IUOを文字列補間で使用するときに強制的にラップ解除する場合は、単に強制的にアンラップ演算子!
を使用できます。
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
がnil
の場合、どちらもクラッシュします。