【问题标题】:Checking for nil Value in Swift Dictionary Extension在 Swift 字典扩展中检查 nil 值
【发布时间】:2016-07-01 07:17:02
【问题描述】:

我在 Playground 文件中有以下代码:

extension Dictionary {

    func test()  {

        for key in self.keys {
            self[key]
        }
    }
}

var dict: [String: AnyObject?] = [
    "test": nil
]

dict.test()

我以后将 for-each 循环中的行称为输出,因为它是相关的。在这个特定的例子中,输出是nil

当我将 for-each 循环更改为如下所示时:

for key in self.keys {
    print(self[key])
}

输出是"Optional(nil)\n"

我真正想做的是检查 nil 的值,但是代码:

for key in self.keys {
    self[key] == nil
}

输出false

我尝试的另一件事是:

for key in self.keys {
    self[key] as! AnyObject? == nil
}

产生错误:

Could not cast value of type 'Swift.Optional<Swift.AnyObject>' to 'Swift.AnyObject'

非常感谢任何帮助!

【问题讨论】:

  • 这是一个非常好的问题,但请您回过头来说一下(1)您尝试做什么以及(2)是什么真的发生了吗?
  • 这是一个示例,演示如何在字典中检查“双重选项”:stackoverflow.com/questions/27225232/…,以及如何区分“键的值不存在”和“键的值存在并且是无”。

标签: swift dictionary null extension-methods optional


【解决方案1】:

你把自己弄得一团糟,因为字典的值可以是 nil .如果密钥丢失,您会得到nil,如果密钥丢失,您会得到nil,并且您打开获取的结果。不幸的是,您正在操场上进行测试,这不是探索差异的场所。

要明白我的意思,请考虑以下几点:

var d : [String:Int?] = ["Matt":1]
let val = d["Matt"]

val 的类型是什么?这是Int?? - 一个 Int 包裹在一个 Optional 中,包裹在另一个 Optional 中。这是因为字典 inside 的值,根据定义,是一个包裹在 Optional 中的 Int,然后通过其键获取值,将其包裹在 another Optional 中。

所以现在让我们走得更远一点,这样做:

var d : [String:Int?] = ["Matt":nil]
let val = d["Matt"]

val 是什么?好吧,操场可能nil,但事实要复杂得多;它是nil 包装在另一个可选中。如果您打印 val,这是最容易看到的,在这种情况下,您得到的不是nil,而是"Optional(nil)"

但如果我们尝试寻找根本不存在密钥的东西,我们会得到一个不同的答案

let val2 = d["Alex"]

确实nil,表示密钥丢失。如果我们打印val2,我们会得到"nil"。 Playground 无法区分(valval2 都显示为 nil),但转换为字符串(这是 print 所做的)显示了差异。

所以问题的一部分是整个双重包装的 Optional 东西,另一部分是 Playground 以非常误导的方式代表了双重包装的 Optional。

道德 1:值类型为 Optional 的字典会变得非常棘手。

道德 2:游乐场是魔鬼的杰作。避开他们。使用真实的应用程序,并使用日志记录到控制台或调试器的变量窗格,以查看实际发生的情况。

【讨论】:

【解决方案2】:

检查字典值是否为nil 仅在以下情况下才有意义 字典值是Optional 类型,这意味着您必须 将您的扩展方法限制在这种情况下。

这可以通过定义一个所有可选类型都符合的协议来实现 to(对比How can I write a function that will unwrap a generic property in swift assuming it is an optional type?,只是对Creating an extension to filter nils from an Array in Swift稍作修改):

protocol OptionalType {  
    typealias Wrapped 
    var asOptional : Wrapped? { get }
}  

extension Optional : OptionalType {  
    var asOptional : Wrapped? {  
        return self 
    }  
}

现在您可以定义一个扩展方法,限制为 可选值类型的字典:

extension Dictionary where Value : OptionalType {

    func test()  {
        for key in self.keys { ... }
    }
}

正如马特已经解释的那样,self[key] 只能是 nil 如果那个键 字典中没有,这不可能在这里发生。那么你 始终可以检索该键的值

            let value = self[key]! 

在那个循环里面。更好的是,枚举键和值:

        for (key, value) in self { ... }

现在value 是字典值(对于key),它的类型 符合OptionalType。使用 asOptional 协议属性 你会得到一个可选的,可以针对 nil 进行测试:

             if value.asOptional == nil { ... }

或与可选绑定一起使用。

将所有这些放在一起:

extension Dictionary where Value : OptionalType {

    func test()  {
        for (key, value) in self {
            if let unwrappedValue = value.asOptional {
                print("Unwrapped value for '\(key)' is '\(unwrappedValue)'")
            } else {
                print("Value for '\(key)' is nil")
            }
        }
    }
}

例子:

var dict: [String: AnyObject?] = [
    "foo" : "bar",
    "test": nil
]

dict.test()

输出:

'test' 的值为 nil 'foo' 的展开值为 'bar'

【讨论】:

  • 您能解释一下asOptional 属性在做什么吗?它是否只是将self 再次包装为可选?我通过创建一个新变量为AnyObject? 进行测试,并将"a" 分配给它。我按原样打印了变量并使用asOptional 并得到了相同的结果。
  • @Alex311:是的,它返回值本身,但作为Wrapped? 以便编译器可以将其视为可选。这是一个“技巧”或“解决方法”(回到forums.developer.apple.com/message/12078),因为你不能定义像extension Dictionary where Value : Optional&lt;T&gt; 这样的东西。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多