Свифт ноль любой! = Ноль - PullRequest
1 голос
/ 30 апреля 2020

Swift 5.1. Рассмотрим следующее.

let x: Any? = nil
let y: Any = x
print("x \(x)")                      // x nil
print("type(of: x) \(type(of: x))")  // type(of: x) Optional<Any>
print("x == nil \(x == nil)")        // x == nil true
print("y \(y)")                      // y nil
print("type(of: y) \(type(of: y))")  // type(of: y) Optional<Any>
print("y == nil \(y == nil)")        // y == nil false

У нас есть две переменные, равные одной и той же вещи - ноль. Они печатают одинаково, их тип печатает одинаково - но один == nil, а другой - нет.

  1. Почему?
  2. При таком Any как можно обнаружить что это на самом деле ноль?

Ответы [ 2 ]

5 голосов
/ 30 апреля 2020

Представьте себе Any и Optional<Wrapped> как коробки.

  1. Any - это непрозрачная коробка, которая может содержать что угодно . Вы можете пролить свет на то, что внутри, пытаясь разыграть его, используя as?.
  2. Optional<Wrapped> - прозрачный ящик. Он позволяет увидеть, что его содержимое имеет значение Optional<Wrapped>.some(wrapped) или Optional<Wrapped>.none().

Optional<Any> и Any, содержащие Optional<Wrapped>, не одно и то же.

1. x

- это значение Optional<Any>.none, иначе nil. nil печатается, так что это имеет смысл.

2. type(of: x)

- это тип x, Optional<Any>, как мы и ожидали.

3. x == nil

вызывает оператор == типа (T, T) -> Bool. Поскольку оба аргумента должны быть одного типа, и поскольку x имеет значение Optional<Any>.none (типа Optional<Any>), nil определяется как Optional<Any>.none. Эти два значения эквивалентны, поэтому мы получаем true, как и ожидалось.

4. y

- это значение типа Any. Во время компиляции больше ничего не известно о y. Так уж получилось, что значение, скрытое непрозрачной шторкой Any, составляет x. print реализован, чтобы "видеть сквозь" Any instances, чтобы показать вам, что на самом деле там.

5. type(of: y)

делает что-то похожее на print. Он использует информацию о типе времени выполнения, чтобы точно определить, какое значение времени выполнения хранится в y. Тип *1039* stati *1168* равен Any, но type(of: y) показывает нам, что тип времени выполнения его значения равен Optional<Any>.

6. y == nil

Здесь немного сложнее.

Выбор перегрузки вызываемой функции или оператора выполняется во время компиляции на основе статически известных типов операнды. То есть операторы и функции не отправляются динамически в зависимости от типов их аргументов, от способа их динамической отправки в зависимости от типа их объектов (например, foo in foo.bar()).

Выбранная здесь перегрузка ==(lhs: Wrapped?, rhs: _OptionalNilComparisonType) -> Bool. Вы можете увидеть его реализацию в строках 449-481 из Optional.swift.

Его левый операнд равен Wrapped?, он же Optional<Wrapped>.

Его правый операнд _OptionalNilComparisonType. Это специальный тип, который соответствует ExpressibleByNilLiteral.

Необязательно, это один тип, который соответствует ExpressibleByNilLiteral, что означает, что вы можете написать let x: Optional<Int> = nil, и компилятор может использовать этот протокол, чтобы понять, что nil должно означать Optional<Int>.none. Протокол ExpressibleByNilLiteral существует, чтобы позволить этому поведению быть расширяемым другими типами. Например, вы можете представить себе инфраструктуру взаимодействия Python, где вы хотели бы иметь возможность сказать let none: PyObject = nil, которая может быть передана в Python и преобразована в None, Python в нулевой тип.

Здесь происходит то, что левая сторона (y, типа Any) повышается до типа Any?. Повышенное значение имеет тип Optional<Any>.some(old_value_of_y). Правая часть, nil, используется для создания экземпляра значения _OptionalNilComparisonType, эквивалентного вызову _OptionalNilComparisonType.init(nilLiteral: ()).

Таким образом, ваш сайт вызова эквивалентен:

Optional<Any>.some((Optional<Any>.none) as Any) == _OptionalNilComparisonType(nilLiteral: ()) /*
↑                  ↑↖︎_the value of x_↗︎       ↑↑
↑                  ↖︎_the value of y _________↗︎↑
↖︎_the value of y after promotion to optional__↗︎ */

В соответствии с реализацией, левая сторона - some, правая сторона - nil, и поэтому они неравны.

Вы можете сказать:

Ну, это довольно неочевидное поведение, это не то, что я хотел.

На что я бы ответил:

Не игнорируйте ошибки компилятора, они Вы правы:

предупреждение: сравнение необязательного значения типа Any с nil всегда возвращает false

1 голос
/ 30 апреля 2020

В Swift 5.2 компилятор генерирует предупреждения, объясняющие, что происходит:

let y: Any = x // "Expression implicitly coerced from 'Any?' to 'Any'"
print("y == nil \(y == nil)")  // "Comparing non-optional value of type 'Any' to 'nil' always returns false"

При наличии такого Any, как можно обнаружить, что на самом деле это ноль?

Неопционные не равны нулю.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...