Лечение вынужденного понижения как необязательного никогда не приведет к "нолю",
Я поигрался со 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()
может возвращать только строку, поэтому я предполагаю, что это идеальная ситуация для принудительной формы понижающего приведения. Так почему компилятор выдает мне предупреждение?
Ответы
Ответ 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 Так что это видно даже в ситуациях, когда очевидно, что вы делаете и правильно.