将强制降级视为可选将永远不会产生'nil'

Pam*_*oes 33 downcast optional swift forced-unwrapping

我一直在玩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
Run Code Online (Sandbox Code Playgroud)

Apple的文档说明如下

由于向下转换可能会失败,因此类型转换运算符有两种不同的形式.可选表单as?将返回您尝试向下转换的类型的可选值.强制形式as尝试向下倾斜并强制将结果作为单个复合动作展开.

我不清楚为什么在这里使用as?而不是as正确的.一些测试显示,如果我更改test()以返回Int而不是String,如果我继续使用,代码将退出并出现错误as.如果我切换到使用as?那么代码将继续正常执行并跳过该语句(dict将保持为空).但是,我不确定为什么这是更可取的.在我看来,我宁愿程序退出并出现错误并让我知道演员表不成功然后只是忽略错误的声明并继续执行.

根据文档,我应该使用强制形式"只有当你确定转发将永远成功时".在这种情况下,我确信向下转换将永远成功,因为我知道test()只能返回一个字符串,所以我认为这是强制形式的向下转换的完美情况.那么为什么编译器会给我一个警告呢?

Rya*_*yan 45

让我们仔细看看你的最后一行,并将其爆炸以查看发生了什么:

let temporaryAnyObject = test()
let temporaryString = temporaryAnyObject as String
dict["test"] = temporaryString
Run Code Online (Sandbox Code Playgroud)

错误发生在第二行,您告诉编译器强制执行temporaryAnyObject肯定是String.代码仍将编译(假设您不将警告视为错误),但如果temporaryAnyObject实际上不是,则会崩溃String.

这种方式as有效,没有?,基本上是说"从现在开始,将这个表达式的结果视为该类型,如果结果实际上是该类型,否则我们变得不一致,不能再运行了.

as?工作方式(使用?)说"从现在开始,将此表达式的结果视为该类型,如果结果实际上是该类型,否则此表达式的结果是nil.

所以在我上面的爆炸示例中,如果test()确实返回a String,那么asdowncast成功,temporaryString现在是a String.如果test()没有返回a String,但是说一个Int或者没有从String中as继承的任何其他东西,那么失败并且代码不再能继续运行.

这是因为,作为完全控制的开发人员,您通过不放置可选?指示符告诉系统以这种方式运行.该as命令特别意味着您不能容忍可选行为,并且您需要向下转换才能工作.

如果你已经把?,然后temporaryStringnil,第三行会简单的从词典中删除"测试"键/值对.

这可能看起来很奇怪,但这只是因为这是许多语言的相反默认行为,比如Obj-C,默认情况下将所有内容视为可选,并依赖于您自己的检查和断言.

编辑 - Swift 2更新

从Swift 2开始,强制的,可移动的向下转换操作符as已被删除,并被替换为as!,这是非常迅捷的.行为是一样的.

  • 我理解你所说的"as"vs"as?" 但我仍然不明白警告出现的原因.此外,如果我将我的代码分成三行,就像你概述的那样,警告实际上就会消失.它仅在语句出现在一行时显示. (7认同)
  • 随着昨天对Swift编译器的更新,警告现在提供了一种解决方案,将向下转换放在括号中,而不是用可选的向下转换替换它. (2认同)

Hon*_*ney 6

您可以从两个角度解决此警告。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

}
Run Code Online (Sandbox Code Playgroud)

基本上你告诉自己(和编译器)我想nil安全地处理s 但在下一行,你说不,我不!

编译器会给出警告:

将强制向下转换为 'T' 视为可选将永远不会产生 'nil'

另一种方法是删除 ?

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

    return UIViewController() as! T // I will crash on nils
}
Run Code Online (Sandbox Code Playgroud)

话虽如此,最好的方法可能是不使用强制转换,而只需执行以下操作:

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

    return UIViewController() as? T // I won't crash on nils
}
Run Code Online (Sandbox Code Playgroud)