【问题标题】:Treating a forced downcast as optional will never produce 'nil'将强制向下转换视为可选将永远不会产生“零”
【发布时间】:2014-08-05 02:13:13
【问题描述】:

我一直在玩 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() 只能返回一个字符串,所以我认为这是强制向下转换形式的完美情况。那么为什么编译器会给我一个警告呢?

【问题讨论】:

  • 首先,有很多 Cocoa 类型桥接,因为没有import Foundation,它甚至无法编译,因为String 不是类类型并且与@ 不兼容987654334@。如果您将最后一个String 更改为NSString,警告就会消失,这意味着您在as 中使用非类类型String 可能是个问题。
  • @newacct 有趣的是,如果在 Dictionary 定义或向下转换中将 String 替换为 NSString,警告就会消失。但是,如果在 both 位置将 String 替换为 NSString,警告仍然存在。
  • 注意:自 Swift 2 以来,as 的许多功能已被删除,并被 as! 取代。所以当你使用as!时你会得到同样的错误(使用as不会再产生这个错误了)

标签: swift optional downcast forced-unwrapping


【解决方案1】:

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

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

错误在第二行,你告诉编译器强制temporaryAnyObject 绝对是String。代码仍然可以编译(假设您不将警告视为错误),但如果temporaryAnyObject 实际上不是String,则会崩溃。

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

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

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

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

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

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

编辑 - Swift 2 更新

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

【讨论】:

  • 我明白您所说的“as”与“as”是什么意思?但我仍然不明白为什么会出现警告。此外,如果我像您概述的那样将代码分成三行,则警告实际上会消失。它仅在语句出现在一行中时显示。
  • 啊,你是对的。嗯,这实际上是因为我在爆炸中没有错。这实际上是:let temporaryAnyObject: AnyObject = test(); let temporaryString: String? = temporaryAnyObject as String; dict["test"] = temporaryString,正如您所见,它给出了相同的警告。
  • 而原因是因为dict["test"] = * 期待一个字符串?,所以我刚刚发布的更改显式类型为字符串?,而不是字符串!。将“as”视为一个函数。 “as”函数返回一个字符串!而“作为?”以字符串形式返回?
  • 这源于您可以通过调用 dict["apples"] = nil 来删除一个键(比如说“apples”)。
  • 随着昨天对 Swift 编译器的更新,警告现在提供了一个解决方案,将向下转换放在括号中,而不是用可选的向下转换替换它。
【解决方案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
}

【讨论】:

  • 不强制转换为我工作(例如as?),谢谢!
【解决方案3】:

看起来这个警告已经存在几年了... https://bugs.swift.org/browse/SR-4209 因此,即使在您所做的和正确的事情很明显的情况下,它也会显示出来。

【讨论】:

    猜你喜欢
    • 2021-09-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-01
    相关资源
    最近更新 更多