Ответ 1
Почти всегда нет необходимости проверять, не является ли опциональным nil
. В значительной степени единственный раз, когда вам нужно это сделать, если его nil
-ness - это единственное, о чем вы хотите знать - вам не важно, в чем ценность, просто это не nil
.
В большинстве других случаев существует немного сокращенного сокращения, которое может более безопасно и кратко выполнить задачу внутри if
для вас.
Использование значения, если оно не является nil
Вместо:
let s = "1"
let i = Int(s)
if i != nil {
print(i! + 1)
}
вы можете использовать if let
:
if let i = Int(s) {
print(i + 1)
}
Вы также можете использовать var
:
if var i = Int(s) {
print(++i) // prints 2
}
но обратите внимание, что i
будет локальной копией - любые изменения в i
не повлияют на значение внутри оригинала.
Вы можете развернуть несколько опций в пределах одного if let
, а более поздние могут зависеть от более ранних:
if let url = NSURL(string: urlString),
data = NSData(contentsOfURL: url),
image = UIImage(data: data)
{
let view = UIImageView(image: image)
// etc.
}
Вы также можете добавить предложения where
к развернутым значениям:
if let url = NSURL(string: urlString) where url.pathExtension == "png",
let data = NSData(contentsOfURL: url), image = UIImage(data: data)
{ etc. }
Замена nil
по умолчанию
Вместо:
let j: Int
if i != nil {
j = i
}
else {
j = 0
}
или
let j = i != nil ? i! : 0
вы можете использовать оператор nil-coalescing, ??
:
// j will be the unwrapped value of i,
// or 0 if i is nil
let j = i ?? 0
Приравнивание необязательного с необязательным
Вместо:
if i != nil && i! == 2 {
print("i is two and not nil")
}
вы можете проверить, равны ли опции с необязательными значениями:
if i == 2 {
print("i is two and not nil")
}
Это также работает со сравнением:
if i < 5 { }
nil
всегда равен другим nil
s и меньше любого значения не nil
.
Будьте осторожны! Здесь могут быть найдены:
let a: Any = "hello"
let b: Any = "goodbye"
if (a as? Double) == (b as? Double) {
print("these will be equal because both nil...")
}
Вызов метода (или считывания свойства) на необязательном
Вместо:
let j: Int
if i != nil {
j = i.successor()
}
else {
// no reasonable action to take at this point
fatalError("no idea what to do now...")
}
вы можете использовать необязательную цепочку, ?.
:
let j = i?.successor()
Примечание. j
также будет необязательным для учета сценария fatalError
. Позже вы можете использовать один из других методов в этом ответе, чтобы обрабатывать необязательность j
s, но вы можете часто откладывать фактическое развертывание своих опций до гораздо позже или иногда не на всех.
Как следует из названия, вы можете связать их, чтобы вы могли написать:
let j = s.toInt()?.successor()?.successor()
Дополнительная цепочка также работает с индексами:
let dictOfArrays: ["nine": [0,1,2,3,4,5,6,7]]
let sevenOfNine = dictOfArrays["nine"]?[7] // returns {Some 7}
и функции:
let dictOfFuncs: [String:(Int,Int)->Int] = [
"add":(+),
"subtract":(-)
]
dictOfFuncs["add"]?(1,1) // returns {Some 2}
Назначение свойства на необязательном
Вместо:
if splitViewController != nil {
splitViewController!.delegate = self
}
вы можете назначить через дополнительную цепочку:
splitViewController?.delegate = self
Только если splitViewController
не соответствует nil
, произойдет присвоение.
Использование значения, если оно не является nil
, или bailing (новое в Swift 2.0)
Иногда в функции есть короткий код кода, который вы хотите записать, чтобы проверить опцию, и если его nil
, выйдите из функции раньше, в противном случае продолжайте движение.
Вы можете написать это следующим образом:
func f(s: String) {
let i = Int(s)
if i == nil { fatalError("Input must be a number") }
print(i! + 1)
}
или во избежание разворота силы, например:
func f(s: String) {
if let i = Int(s) {
print(i! + 1)
}
else {
fatalErrr("Input must be a number")
}
}
но гораздо лучше сохранить код обработки ошибок в верхней части проверки. Это также может привести к неприятному гнезду ( "пирамида гибели" ).
Вместо этого вы можете использовать guard
, который похож на if not let
:
func f(s: String) {
guard let i = Int(s)
else { fatalError("Input must be a number") }
// i will be an non-optional Int
print(i+1)
}
Часть else
должна выйти из области защищенного значения, например. a return
или fatalError
, чтобы гарантировать, что охраняемое значение будет действительным для оставшейся части области.
guard
не ограничивается областью действия. Например, следующее:
var a = ["0","1","foo","2"]
while !a.isEmpty {
guard let i = Int(a.removeLast())
else { continue }
print(i+1, appendNewline: false)
}
печатает 321
.
Зацикливание по элементам, отличным от нуля, в последовательности (новая в Swift 2.0)
Если у вас есть последовательность опций, вы можете использовать for case let _?
для итерации по всем необязательным элементам:
let a = ["0","1","foo","2"]
for case let i? in a.map({ Int($0)}) {
print(i+1, appendNewline: false)
}
выводит 321
. Это использует синтаксис соответствия шаблону для необязательного, который является именем переменной, за которым следует ?
.
Вы также можете использовать это сопоставление в инструкциях switch
:
func add(i: Int?, _ j: Int?) -> Int? {
switch (i,j) {
case (nil,nil), (_?,nil), (nil,_?):
return nil
case let (x?,y?):
return x + y
}
}
add(1,2) // 3
add(nil, 1) // nil
Цикл, пока функция не вернет nil
Скорее как if let
, вы также можете написать while let
и цикл до nil
:
while let line = readLine() {
print(line)
}
Вы также можете написать while var
(применяются аналогичные оговорки к if var
).
where
также работают здесь (и завершают цикл, а не пропускают):
while let line = readLine()
where !line.isEmpty {
print(line)
}
Передача необязательного значения в функцию, которая принимает необязательный параметр и возвращает результат
Вместо:
let j: Int
if i != nil {
j = abs(i!)
}
else {
// no reasonable action to take at this point
fatalError("no idea what to do now...")
}
вы можете использовать опцию map
operator:
let j = i.map { abs($0) }
Это очень похоже на необязательную цепочку, но если вам нужно передать необязательное значение в функцию в качестве аргумента. Как и в случае с необязательной цепочкой, результат будет необязательным.
Это хорошо, если вы хотите, чтобы это было возможно. Например, reduce1
похож на reduce
, но использует первое значение в качестве семени, возвращая необязательный, если массив пуст. Вы можете написать его так (используя ключевое слово guard
из более раннего):
extension Array {
func reduce1(combine: (T,T)->T)->T? {
guard let head = self.first
else { return nil }
return dropFirst(self).reduce(head, combine: combine)
}
}
[1,2,3].reduce1(+) // returns 6
Но вместо этого вы можете map
свойство .first
и вернуть это:
extension Array {
func reduce1(combine: (T,T)->T)->T? {
return self.first.map {
dropFirst(self).reduce($0, combine: combine)
}
}
}
Передача необязательного значения в функцию, которая принимает необязательный параметр и возвращает результат, избегая раздражающих двойных опций
Иногда вам нужно что-то похожее на map
, но функция, которую вы хотите вызвать сама, возвращает необязательный. Например:
// an array of arrays
let arr = [[1,2,3],[4,5,6]]
// .first returns an optional of the first element of the array
// (optional because the array could be empty, in which case it nil)
let fst = arr.first // fst is now [Int]?, an optional array of ints
// now, if we want to find the index of the value 2, we could use map and find
let idx = fst.map { find($0, 2) }
Но теперь idx
имеет тип Int??
, double-optional. Вместо этого вы можете использовать flatMap
, который "выравнивает" результат в один дополнительный параметр:
let idx = fst.flatMap { find($0, 2) }
// idx will be of type Int?
// and not Int?? unlike if `map` was used