Назначить значение для дополнительного словаря в 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
}

Ответ 1

Момент лампочки - это когда вы понимаете, что дополнительный словарь не словарь. Опционально что-то не так! Это необязательно! И это все. Необязательный сам по себе тип. Необязательный - это просто перечисление, обертывающее возможные случаи nil и некоторое значение. Обернутое значение представляет собой совершенно другой объект, хранящийся внутри.

Так что Факультативно ничего не действует, как тип этой вещи. Дело не в этом! Это просто необязательно. Единственный способ понять это - развернуть его.

То же самое относится к неявно развернутому Необязательному; разница заключается только в том, что неявно развернутый Необязательный желает готовить (выставлять) завернутое значение "автоматически". Но он по-прежнему завернут. И, как заметил Брайан Чен, оно завернуто неизменно; Опция просто держит ее за вас - она ​​не дает вам места для игры с ней.

Ответ 2

вы можете использовать этот код

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.

Один из способов вызова метода по опциональному - использовать ! i.e. 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:) (метод mutating) не может быть вызван на нем

так что произошло, вероятно, что 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

и это привело меня к другому вопросу, почему Array и Dictionary - тип значения (struct)? напротив NSArray и NSDictionary, которые являются ссылочным типом (классом)

Ответ 3

Это потому, что ваш словарь не является обязательным. Если он равен нулю, вы не добавите к нему записи.

Вы можете сделать так:

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"

Ответ 4

Я попробовал это для Swift 3.1, и он работал:

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