Nil 检查并不总是适用于 Swift 中的 Any

Posted

技术标签:

【中文标题】Nil 检查并不总是适用于 Swift 中的 Any【英文标题】:Nil checks not always working for Any in Swift 【发布时间】:2018-01-06 14:25:05 【问题描述】:

当我使用 Any 类型时,我对 Swift 如何检查 nil 感到困惑。 这是一个例子:

let testA: Any? = nil
let testB: Any = testA as Any
let testC: Any? = testB

if testA != nil 
    print(testA) // is not called as expected

if testB != nil 
    print(testB) // prints "nil"

if testC != nil 
    print(testC) // prints "Optional(nil)"

testA 按预期工作。变量为零,因此条件为假。

testB 与预期不同。变量为 nil,如 print 调用所示。但是条件testB != nil 的计算结果为真。为什么会这样?

testC​​strong> 也让我感到困惑,因为它是 testC = testB = testA。那么为什么它的行为应该与 testA 不同呢?

我需要如何编写 if 条件 if testB ...if testC ... 才能不为真。 我正在寻找一种不需要我知道类型的解决方案,比如...

if let testB = testB as String

编辑:我正在 Xcode 9.1 Playground 文件中使用 Swift 4 对此进行测试。

编辑2: 有关我要解决的实际问题的一些信息。 我得到了一个由 JSON 解析器创建的 [String: Any?] 类型的字典。我想检查给定键的值是否为 nil,但是当键存在且值为 Optional(nil) 时,它不起作用。

例子:

var dict = [String: Any?]()
var string = "test"
var optionalString: String?
dict["key1"] = string
dict["key2"] = optionalString

if dict["key2"] != nil 
    print(dict["key2"]) // should not be executed, but returns Optional(nil)

【问题讨论】:

奇怪的是,let testB: Any = testA as Any 首先没有给出任何编译器错误。 testA 是可选的,testB 不是,因此将testA 分配给testB 至少应该告诉您这样做不安全,您需要使用as?as! 进行转换。 let testB: Any = testA 应该警告您可选类型的侵蚀(然后您通过说as Any 使其静音)。基本上,您在这里处理的根本问题是,除非编译器知道它是可选的,否则您不能将其视为可选的。说let testC: Any? = testB 只会使编译器隐式地将testB 包装在另一层可选性中。您要在这里解决的实际问题是什么? @florieger 啊,在这种情况下,你只想打开两次。 if let value = dict["key"] 为您提供 value 类型的 Any?,这意味着字典具有该值。如果你然后说if let nonNilValue = value,你会得到一个Any,这意味着值本身不是零。您也可以说dict["key"] ?? nil 将两层可选性浓缩为一层。比较***.com/q/29299727/2976878、***.com/q/33049246/2976878 相关:Is it possible to cast Any to an Optional? - 您的 testB 不是可选的,需要反射才能再次“提取”可选。 继 @MartinR 所写的内容(专注于人为的例子);也可能对the following Q&A 感兴趣,关于使用运行时自省来查询Any 实例是否包装了任何类型的Optional<...> 实例。 【参考方案1】:

在 Swift 中,nil 实际上是一个具体的值(枚举类型)。属于Any 类型的testB 持有enum Optional,其值为none,因此条件testB != nil 为真。

这解决了testBAny 是如何保持nil 值的谜团。

针对您的实际问题,我在 Storyboard(Xcode 9.2) 中尝试了这段代码,它按预期工作。

var dict = [String: Any]()
var string = "test"
var optionalString: String?
dict["key1"] = string
dict["key2"] = optionalString

if let value = dict["key2"] 
    print(value) // doesn't get executed

对于 testB 和 testC,似乎 == 检查与 nil 应该提供一个解决方案,但是,因为二进制操作数不能与两个 Any 一起使用?操作数,我们不能使用 ==。

虽然使用 switch-case 能够给出正确的结果:

switch testB 
case Optional<Any>.none:
    print("value is none")
default:
    print("value is not none")


switch testC! 
case Optional<Any>.none:
    print("value is none")
default:
    print("value is not none")

O/P: 值为无 值为无

【讨论】:

感谢您提供解决方案,我怎样才能使其与testBtestC 一起使用。 @florieger:更新了答案 那么内存中的同一件事可以在一种情况下快速解释为nil,在另一种情况下解释为非nil?绝对疯狂的语言设计决策。 @algrid:它更像是 Any 类型的东西,但我同意它非常令人困惑。 正如***.com/a/61530672/513038 中解释的那样,我认为问题实际上发生在(Any) != nil - 编译时类型Any 的表达式在编译时被包裹在另一层Optional - 所以Optional 的第一层不是nil,它包含一个Optional,即nil。 (技术上.none。)在运行时,String? 被包装成String??。如果我的评论没有意义,也许可以阅读链接的答案。

以上是关于Nil 检查并不总是适用于 Swift 中的 Any的主要内容,如果未能解决你的问题,请参考以下文章

React 状态更改并不总是适用于移动网络

Swift 在一行中返回关联的枚举值或 nil

AngularJS:范围值改变,但并不总是适用于 ng-class

在 Ruby on Rails 中检查视图中的 nil

Lua:明确检查nil后字符串为nil

存在的 ViewController 并不总是有效