Ответ 1
Согласно SE-0054, ImplicitlyUnwrappedOptional<T>
больше не является отдельным типом; теперь есть только Optional<T>
.
Объявления по-прежнему могут быть аннотированы как неявно развернутые необязательные T!
, но при этом просто добавляется скрытый атрибут, информирующий компилятор о том, что их значение может быть принудительно развернуто в контекстах, которые требуют их развернутого типа T
; их фактический тип теперь T?
,
Итак, вы можете подумать об этой декларации:
var str: String!
как на самом деле выглядит так:
@_implicitlyUnwrapped // this attribute name is fictitious
var str: String?
Только компилятор видит этот атрибут @_implicitlyUnwrapped
, но он допускает неявное развертывание значения str
в контекстах, которые требуют String
(его тип unwrapped):
// '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
для оценки сегмента интерполяции строки:
/// 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
.