Рассмотрение вынужденного подавления как необязательного никогда не приведет к "нулю"


Я играл со Свифтом и обнаружил, что когда я бросаю вниз объект, который нужно вставить в словарь, я получаю странное предупреждение: 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 вместо строки, код завершится с ошибкой, если я продолжу использовать as. Если я переключусь на using as?, то код продолжит выполнение нормально и пропустит эту инструкцию (dict останется пустым). Однако я не уверен, почему это предпочтительнее. В моем мнение, я бы предпочел, чтобы программа вышла с ошибкой и дала мне знать, что приведение было неудачным, а затем просто проигнорировала ошибочное утверждение и продолжила выполнение.

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

2 ответа:

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

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

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

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

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

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

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

Если бы вы поставили ?, то temporaryString было бы nil, а третья строка просто удалила бы" тестовый " ключ / значение пара из словаря.

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

Edit-Swift 2 Update

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

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

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

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

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

}

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

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

Рассмотрение принудительного понижения до '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
}