在Swift中为可选字典赋值

时间:2014-06-07 10:35:41

标签: dictionary swift

我在Swift中使用可选字典找到了一些令人惊讶的行为。

var foo:Dictionary<String, String>?

if (foo == nil) {
    foo = ["bar": "baz"]
}
else {
    // Following line errors with "'Dictionary<String, String>?' does
    // not have a member named 'subscript'"
    foo["qux"] = "quux"
}

我已经玩了很多,试图弄清楚我可能会遗漏什么,但似乎没有任何东西使这段代码按预期工作,使得字典不是可选的。我错过了什么?

我能得到的最接近的是以下,但这当然是荒谬的。

var foo:Dictionary<String, String>?

if (foo == nil) {
    foo = ["bar": "baz"]
}
else if var foofoo = foo {
    foofoo["qux"] = "quux"
    foo = foofoo
}

4 个答案:

答案 0 :(得分:31)

灯泡时刻就是你意识到一个可选词典不是一个词典。一个可选的东西不是那个东西!这是一个可选的!!这就是全部。可选本身就是一种类型。一个可选项只是一个枚举,包含可能的案例nil和一些值。包装的值是一个完全不同的对象,存储在里面。

因此,一个可选的任何不会像那种东西那样行事。不是那个东西!这只是一个选择。解决问题的唯一方法就是解开它。

隐式解包的Optional也是如此;区别在于隐式展开的Optional愿意自动生成(暴露)包装的值&#34;自动&#34;。但它实际上仍然是包装好的。并且,正如布莱恩·陈所观察到的那样,它是不可改变地包裹着的; “可选”只是为您举办 - 它没有给您一个可以玩的地方。

答案 1 :(得分:7)

您可以使用此代码

if var foofoo = foo {
    foofoo["qux"] = "quux"
    foo = foofoo
} else {
    foo = ["bar": "baz"]    
}

使用此代码

var foo:Dictionary<String, String>? = Dictionary()
foo[""]=""

error: 'Dictionary<String, String>?' does not have a member named 'subscript'
foo[""]=""
^

错误消息让我觉得Dictionary<String, String>?没有实现subscript方法,因此您需要在能够使用subscript之前将其解包。

在可选项上调用方法的一种方法是使用!,即foo![""],但是......

var foo:Dictionary<String, String>? = Dictionary()
foo![""]=""

error: could not find member 'subscript'
foo![""]=""
~~~~~~~~^~~

,而

var foo:Dictionary<String, String>? = Dictionary()
foo![""]

作品


有趣的是这些代码无法编译

var foo:Dictionary<String, String>! = Dictionary() // Implicitly unwrapped optional
foo[""]=""

error: could not find an overload for 'subscript' that accepts the supplied arguments
foo[""]=""
~~~~~~~^~~

var foo:Dictionary<String, String>! = Dictionary() // Implicitly unwrapped optional
foo.updateValue("", forKey: "")

immutable value of type 'Dictionary<String, String>' only has mutating members named 'updateValue'
foo.updateValue("", forKey: "")
^   ~~~~~~~~~~~

最后一条错误消息最有趣,它是说Dictionary是不可变的,因此无法调用updateValue(forKey:)(变异方法)

所以发生的事情可能是Optional<>Dictionary存储为不可变对象(let)。因此,即使Optional<>它是可变的,您也无法直接修改基础Dictionary对象(无需重新分配Optional对象)


并且此代码有效

class MyDict
{
    var dict:Dictionary<String, String> = [:]

    subscript(s: String) -> String? {
        get {
            return dict[s]
        }
        set {
            dict[s] = newValue
        }
    }
}

var foo:MyDict? = MyDict()
foo!["a"] = "b" // this is how to call subscript of optional object

这引出了另一个问题,为什么ArrayDictionary是值类型(struct)?与NSArrayNSDictionary相反,它们是引用类型(类)

答案 2 :(得分:0)

这是因为您的词典是可选的。如果它为零,则无法为其添加条目。

你可以这样做:

var dict: [String : String]?
if let dict = dict {
  dict["key"] = "value" // add a value to an existing dictionary
} else {
  dict = ["key" : "value"] // create a dictionary with this value in it
}

或者,如果给你一个可选字典,例如HTTPHeaders - 在AlamoFire中是一个[String:String]字典 - 并且你想要添加一个值,如果它是非零的,或者创建它有了这个值,如果它没有,你可以这样做:

let headers: HTTPHeaders? // this is an input parameter in a function for example

var customHeaders: HTTPHeaders = headers ?? [:] // nil coalescing
customHeaders["key"] = "value"

答案 3 :(得分:0)

我为Swift 3.1尝试了这个,它起作用了:

if (myDict?[key] = value) == nil {
    myDict = [key: value]
}