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