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

Лечение вынужденного понижения как необязательного никогда не приведет к "нолю",

Я поигрался со Swift и обнаружил, что при приведении объекта к вставке в словарь я получаю странное предупреждение: Treating a forced downcast to 'String' as optional will never produce 'nil'. Если я заменю as на as? тогда предупреждение уходит.

func test() -> AnyObject! {
  return "Hi!"
}

var dict = Dictionary<String,String>()
dict["test"]=test() as String

Документация Apple гласит следующее

Поскольку даункастинг может потерпеть неудачу, оператор приведения типа может быть двух разных форм. Необязательная форма, as? , возвращает необязательное значение типа, который вы пытаетесь уменьшить. Принудительная форма as пытается выполнить переход вниз и принудительно разворачивает результат как одно сложное действие.

Мне неясно, почему использовать as? а не as здесь правильно. Некоторое тестирование показывает, что если я изменю test(), чтобы он возвращал Int вместо String, код завершит работу с ошибкой, если я продолжу использовать as. Если я перейду на использование as? тогда код продолжит выполнение в обычном режиме и пропустит этот оператор (dict останется пустым). Однако я не уверен, почему это предпочтительнее. По моему мнению, я бы предпочел, чтобы программа завершилась с ошибкой и дала мне знать, что приведение не было успешным, а затем просто проигнорировал ошибочное утверждение и продолжил выполнение.

Согласно документации, я должен использовать принудительную форму "только тогда, когда вы уверены, что уныние всегда будет успешным". В этом случае я уверен, что обратное преобразование всегда будет успешным, поскольку я знаю, что test() может возвращать только строку, поэтому я предполагаю, что это идеальная ситуация для принудительной формы понижающего приведения. Так почему компилятор выдает мне предупреждение?

4b9b3361

Ответ 1

Позвольте более подробно рассмотреть вашу последнюю строку и взорвать ее, чтобы увидеть, что происходит:

let temporaryAnyObject = test()
let temporaryString = temporaryAnyObject as String
dict["test"] = temporaryString

Ошибка во второй строке, где вы сообщаете компилятору, что temporaryAnyObject определенно является String. Код будет компилироваться (при условии, что вы не рассматриваете предупреждения как ошибки), но будет аварийно, если temporaryAnyObject на самом деле не является String.

Как работает as, без ?, в основном говорит "отныне, рассматривайте результат этого выражения как этот тип, ЕСЛИ результат на самом деле имеет этот тип, иначе мы стали непоследовательными и можем больше не выполняются.

Способ as? работает (с ?) говорит "отныне обрабатывать результат этого выражения как этот тип. ЕСЛИ результат на самом деле имеет этот тип, иначе результат этого выражения будет nil.

Итак, в приведенном выше примере в разобранном виде, если test() возвращает a String, то as downcast завершается успешно, а temporaryString теперь String. Если test() не возвращает String, но скажите Int или что-либо еще, не подклассифицированное из String, тогда as завершается с ошибкой, и код больше не может продолжать работать.

Это связано с тем, что, поскольку разработчик полностью контролирует вас, вы сказали, что система ведет себя таким образом, не помещая дополнительный индикатор ?. Команда as специально означает, что вы не допускаете опционального поведения, и вам нужно, чтобы downcast работал.

Если вы положили ?, тогда temporaryString будет nil, а третья строка просто удалит пару/пару из теста "test" из словаря.

Это может показаться странным, но это только потому, что это противоположное поведение по умолчанию многих языков, таких как Obj-C, которые по умолчанию обрабатывают все как необязательные, и полагаются на вас, чтобы разместить свои собственные проверки и подтверждения.

Изменить - Быстрое обновление 2

Начиная с Swift 2, принудительный, отказоустойчивый оператор as был удален и заменен на as!, который намного быстрее. Поведение одного и того же.

Ответ 2

Вы можете решить это предупреждение с двух сторон. 1. Возвращаемое значение 2. Тип, который вы ожидаете вернуть. Другой ответ говорит об 1-м угле. Я говорю о втором угле

Это связано с тем, что вы возвращаете принудительное разворачивание и литье для необязательного. Компилятор выглядит так: "Если вы действительно хотите просто принудительно применить все опции, то почему бы просто не сделать ожидаемый возвращаемый параметр не факультативным"

Например, если вы написали

func returnSomething<T> -> T?{ // I'm an optional, I can handle nils SAFELY and won't crash.

return UIViewController as! T // will never return a safe nil, will just CRASH

}

В основном вы сказали себе (и компилятору), что я хочу безопасно обрабатывать nil, но затем в самой следующей строке вы сказали, что нет, я не делаю!!!

Компилятор выдаст предупреждение:

Обработка принудительного понижения до "T" как необязательного никогда не приведет к "nil"

Альтернативой является удаление ?

func returnSomething<T>() -> T{ // I can't handle nils

    return UIViewController() as! T // I will crash on nils
}

Сказав это, скорее всего, лучший способ - не применять силу и просто делать:

func returnSomething<T>() -> T?{ // I can handle nils

    return UIViewController() as? T // I won't crash on nils
}

Ответ 3

Похоже, что об этом предупреждении уже несколько лет открыто сообщение об ошибке... https://bugs.swift.org/browse/SR-4209 Так что это видно даже в ситуациях, когда очевидно, что вы делаете и правильно.