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
的计算结果为真。为什么会这样?
testCstrong> 也让我感到困惑,因为它是 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
为真。
这解决了testB
的Any
是如何保持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: 值为无 值为无
【讨论】:
感谢您提供解决方案,我怎样才能使其与testB
和testC
一起使用。
@florieger:更新了答案
那么内存中的同一件事可以在一种情况下快速解释为nil,在另一种情况下解释为非nil?绝对疯狂的语言设计决策。
@algrid:它更像是 Any 类型的东西,但我同意它非常令人困惑。
正如***.com/a/61530672/513038 中解释的那样,我认为问题实际上发生在(Any) != nil
- 编译时类型Any
的表达式在编译时被包裹在另一层Optional
- 所以Optional
的第一层不是nil
,它包含一个Optional
,即nil
。 (技术上.none
。)在运行时,String?
被包装成String??
。如果我的评论没有意义,也许可以阅读链接的答案。以上是关于Nil 检查并不总是适用于 Swift 中的 Any的主要内容,如果未能解决你的问题,请参考以下文章