Assign value to optional dictionary in Swift

后端 未结 4 1166
北恋
北恋 2020-12-10 04:42

I\'m finding some surprising behavior with optional dictionaries in Swift.

var foo:Dictionary?

if (foo == nil) {
    foo = [\"bar\": \         


        
相关标签:
4条回答
  • 2020-12-10 05:26

    The lightbulb moment is when you realize that an Optional dictionary is not a Dictionary. An Optional anything is not that thing! It is an Optional!! And that's all it is. Optional is itself a type. An Optional is just an enum, wrapping the possible cases nil and some value. The wrapped value is a completely different object, stored inside.

    So an Optional anything does not act like the type of that thing. It is not that thing! It is just an Optional. The only way to get at the thing is to unwrap it.

    The same is true of an implicitly unwrapped Optional; the difference is just that the implicitly unwrapped Optional is willing to produce (expose) the wrapped value "automatically". But it is still, in fact, wrapped. And, as Bryan Chen has observed, it is wrapped immutably; the Optional is just holding it for you - it is not giving you a place to play with it.

    0 讨论(0)
  • 2020-12-10 05:40

    This is because your Dictionary is optional. If it's nil, you won't add an entry to it.

    You can do this way:

    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
    }
    

    Or, if you are given an optional dictionary, for example HTTPHeaders - which in AlamoFire is a [String : String] dictionary - and you want to either add a value if it's non-nil, or create it with this value if it's nil, you could do like so:

    let headers: HTTPHeaders? // this is an input parameter in a function for example
    
    var customHeaders: HTTPHeaders = headers ?? [:] // nil coalescing
    customHeaders["key"] = "value"
    
    0 讨论(0)
  • 2020-12-10 05:41

    you can use this code

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

    with this code

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

    the error message makes sense to me that Dictionary<String, String>? does not implement subscript method, so you need to unwrap it before able to use subscript.

    one way to call method on optional is use ! i.e. foo![""], but...

    var foo:Dictionary<String, String>? = Dictionary()
    foo![""]=""
    
    error: could not find member 'subscript'
    foo![""]=""
    ~~~~~~~~^~~
    

    whereas

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

    works


    it is interesting these code failed to compile

    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: "")
    ^   ~~~~~~~~~~~
    

    the last error message is most interesting, it is saying the Dictionary is immutable, so updateValue(forKey:) (mutating method) can't be called on it

    so what happened is probably that the Optional<> store the Dictionary as immutable object (with let). So even Optional<> it is mutable, you can't modify the underlying Dictionary object directly (without reassign the Optional object)


    and this code works

    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
    

    and this lead me to another question, why Array and Dictionary are value type (struct)? opposite to NSArray and NSDictionary which are reference type (class)

    0 讨论(0)
  • 2020-12-10 05:42

    I tried this for Swift 3.1 and it worked:

    if (myDict?[key] = value) == nil {
        myDict = [key: value]
    }
    
    0 讨论(0)
提交回复
热议问题