38

我一直在玩 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

苹果的文档说以下

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

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

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

4

3 回答 3

46

让我们仔细看看你的最后一行,然后分解它看看发生了什么:

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

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

工作方式as,没有?,基本上是在说“从现在开始,如果结果实际上是那种类型,则将此表达式的结果视为该类型,否则我们变得不一致并且无法再运行。

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

所以在我上面的分解示例中,如果test()确实返回 a String,那么as向下转型成功,temporaryString现在是 a String。如果test()不返回 a String,而是说 anInt或任何其他不是从 String 子类化的内容,则as失败并且代码无法继续运行。

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

如果您放了?, 那么temporaryString将是nil,并且第三行将简单地从字典中删除“测试”键/值对。

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

编辑 - 斯威夫特 2 更新

从 Swift 2 开始,强制的、可失败的向下转换运算符as已被删除,并被替换as!为 Swiftier。行为是相同的。

于 2014-06-14T20:38:20.557 回答
6

您可以从两个角度解决此警告。1. 你返回的值 2. 你期望返回的类型。另一个答案是关于第一个角度。我说的是第二个角度

这是因为您正在为optional返回一个强制解包和强制转换的值。编译器就像,“如果你真的只想强制强制转换所有可选参数,那么为什么不把预期的返回参数设置为非可选参数”

例如,如果你写

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安全地处理 s 但是在下一行,你说不,我不!

编译器会给出警告:

将强制向下转换为“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
}
于 2017-10-19T19:06:13.430 回答
0

几年来,似乎有关于此警告的错误打开... https://bugs.swift.org/browse/SR-4209因此,即使在您正在做的事情和正确的事情很明显的情况下,它也会显示出来。

于 2019-01-16T15:30:54.243 回答