Подтвердить что ты не робот

Почему не факультативный Любой может содержать nil?

В Swift я могу объявить константу типа Any и поместить в нее String.

let any: Any = "hello world"

Хорошо. С другой стороны, я не могу поместить значение nil в any потому что это не обязательно.

let any: Any = nil

error: nil cannot initialize specified type 'Any' (aka 'protocol<>')
let any: Any = nil
              ^

Отлично. Но почему компилятор позволяет мне писать следующий код?

let couldBeNil: String? = nil
let any: Any = couldBeNil
print(any) // nil

Разве Any не следует правилу Swift, что только Optional var/let могут быть заполнены nil?

Протестировано с Xcode Playground 7.2 + Swift 2.1.1

4b9b3361

Ответ 1

TL; DR; Необязательные параметры в swift преобразуются компилятором в экземпляры Optional перечислений, и, поскольку Any может отображаться на любое значение, его можно использовать для хранения необязательных параметров.


Как Swift представляет опционы? Это делает это путем сопоставления SomeType? Для конкретной реализации Optional перечисления:

Int? => Optional<Int>
String? => Optional<String>

Упрощенное объявление Optional выглядит следующим образом:

enum Optional<T> {
    case none    // nil
    case some(T) // non-nil
}

Теперь переменная типа Any может содержать значение перечисления (или любое другое значение, или даже информацию метатипа), поэтому она должна содержать, например, строку nil String, aka String?.none, aka Optional<String>.none.

Давайте посмотрим, что происходит, хотя. Как мы видим из Optional объявления, nil соответствует .none перечисления .none для всех типов:

nil == Optional<String>.none // true
nil == Optional<Int>.none    // true
[Double]?.none == nil        // also true

Теоретически, вы должны иметь возможность назначить nil для переменной, объявленной как Any. Тем не менее, компилятор не позволяет этого.

Но почему компилятор не позволяет назначить nil для переменной Any? Это потому, что он не может определить, какой тип отображать .none. Optional является универсальное перечисление, поэтому ему нужно что-то для заполнения универсального параметра T, а обычный nil слишком широкий. Какое значение .none следует использовать? Один из Int, другой из String, другой?

Это дает сообщение об ошибке, подтверждающее вышеприведенный абзац:

let nilAny: Any = nil // error: nil cannot initialize specified type 'Any' (aka 'protocol<>')

Следующий код работает и эквивалентен присвоению nil:

let nilAny: Any = Optional<Int>.none

как указано выше, Any переменная фактически содержит допустимое значение Optional перечисления.

Косвенные назначения также работают, поскольку за кадром nil преобразуется в Optional<Type>.none.

var nilableBool: Bool? // nilableBool has the Optional<Bool>.none value
var nilBoolAsAny: Any = nilableBool // the compiler has all the needed type information from nilableBool

В отличие от других языков, в Swift nil соответствует конкретному значению. Но ему нужен тип для работы, чтобы компилятор знал, какой Optional<T>.none он должен выделить. Мы можем думать о ключевом слове как о предоставлении синтаксиса сахара.

Ответ 2

Из Swift Docs: "Any: протокол, к которому все типы неявно соответствуют".

то есть. typealias Any = protocol<>

Итак, когда вы объявляете String?, вы можете думать об этом как Optional<String>, где Optional<T> может быть реализовано как:

enum Optional<T> {
  case Some(T), None
}

И перечислены типы, поэтому они соответствуют протоколу Any. Как указано в комментариях, nil не является типом (и, следовательно, не соответствует Any, это отсутствие значения (и в этом случае ни одно значение не имеет типа).

Опционы выпекаются на языке и не являются регулярными перечислениями, но все равно стоит, что они соответствуют Any, в то время как безличный nil - нет.

Ответ 3

Для тех, кто ищет способ проверить, содержит ли Any nil:

Если вызов po value скажет вам, что переменная Any содержит значение nil следующая строка по-прежнему не работает:

if value == nil { ... } // Returns false and generates a warning

Вы должны преобразовать его в нулевое значение перед проверкой, например, когда Any вероятно, содержит Date:

if Optional<Date>.none == value as? Date { ... } // Returns true and no warning

примечание: я не проверял это, поэтому никаких гарантий, но строка выше может также пройти, когда написано как:

if Optional<Any>.none == value as? Any { ... }