Что это такое и как их можно использовать для защиты вашего кода от сбоев?

Прошел месяц с тех пор, как я начал изучать Swift. Если честно, мне было интересно изучать этот язык. Но когда мне пришлось применить то, что я узнал, это оказалось большим испытанием (К счастью, я люблю испытания!).

Если быть точным, у меня были ожесточенные битвы с Optionals Swift. Я считал Optionals своими злейшими врагами. Но после нескольких дней работы и исследований мы с Optionals наконец пришли к правильному взаимопониманию, и теперь мы лучшие друзья!

Следовательно, эта статья предназначена для тех людей, которые только начали изучать Swift и сталкиваются с трудностями при дружбе с Optionals Swift. Давай начнем!

Что такое варианты?

Необязательные параметры - это тип переменных в Swift, которые либо содержат значение, либо обладают сверхмощностью nil. Другими словами, они могут содержать или не содержать значение. Необязательную переменную можно объявить в Swift следующим образом:

var superOptional: String?
print(superOptional)

Здесь переменная superOptional - это Optional переменная типа String. Это означает, что он может либо содержать в себе какую-то строку, либо иметь значение nil. Прямо сейчас, поскольку мы не присвоили переменной никакого значения, текущее значение superOptional печатается как nil. Однако когда дело доходит до объявления необязательных переменных, дело обстоит иначе. Например, если мы сделаем что-то вроде этого:

var superVariable: String
print(superVariable)

Затем, как только мы завершим написание приведенного ниже кода, IDE начнет кричать на нас, говоря Variable ‘superVariable’ used before being initialized.

Это ясно указывает на то, что нам не нужно инициализировать переменную Optional перед ее использованием (поскольку она автоматически инициализируется с помощью nil), но мы должны инициализировать необязательную или обычную переменную перед ее использованием.

Теперь перейдем к инициализации нашей superOptional переменной.

superOptional = "Hello world!"
print(superOptional)

После добавления этого общий код будет выглядеть так:

var superOptional: String? // old code
print(superOptional)
superOptional = "Hello world!" // new code
print(superOptional)

Результат будет примерно таким:

nil
Optional(“Hello world!”)

Перед тем, как мы инициализировали нашу переменную superOptional, ее значение было nil. Однако после инициализации его строкой “Hello world!” значение больше не было nil, и поэтому на выходе получилось Optional(“Hello world!”).

Но подождите, результат должен был быть just“Hello world!”! Да, именно об этом я подумал, когда создал свою первую Optional переменную. Но поскольку переменная, в которой хранится “Hello world!”, является переменной Optional, она была заключена в Optional(). Есть способы извлечь или развернуть это значение и использовать его. Что я опишу в этой статье.

Распаковка опций

Есть шесть способов развернуть фактические значения (значения, не заключенные в Optional()) из Optional переменных. Они есть:

  1. Распаковка Optionals с использованием условий if-else
  2. Оператор объединения Nil
  3. Принудительное разворачивание
  4. Необязательная привязка (if let)
  5. guard let
  6. Необязательная цепочка

1. Распаковка необязательных элементов с использованием условий if-else

Это способ разворачивания опций, в котором мы используем условия if-else, чтобы проверить, содержит ли Optional значение nil или нет. Если Optional не содержит значения nil, мы разворачиваем его, в противном случае печатаем строку “Default value”. Давайте реализуем это.

var superOptional: String?
superOptional = "Hello world!"
if superOptional != nil {
    print(superOptional)
} else {
    print("Default value")
}

Это может быть знакомый способ раскрыть ценность для многих людей. Однако в Swift есть лучшие и более короткие способы выполнения этой работы. Так что распаковывать таким способом не рекомендуется.

2. Оператор объединения Nil

Использование оператора объединения nil - еще один способ развернуть необязательные параметры. Это лучший и более короткий способ разворачивания, чем использование условий if-else (способ, упомянутый чуть выше в этом разделе статьи). Этот оператор помогает нам либо разворачивать Optional, если он имеет фактическое значение, либо задавая значение по умолчанию, если Optional равно nil. Например:

var superOptional: String?
print(superOptional ?? "Default value") // nil coalescing operator

В приведенном выше примере переменная superOptional имеет значение nil, поскольку она имеет Optional характер, и ей не было присвоено никакого значения. Следовательно, когда он разворачивается с помощью оператора объединения nil, за которым следует строка “Default value”, nil печатается не, а “Default value”. Однако этого не произойдет, если superOptional содержит фактическое значение. Давайте посмотрим на это на следующем примере:

var superOptional: String?
superOptional = "Hello world!"
print(superOptional ?? "Default value")

В приведенном выше примере результат отличается, поскольку superOptional инициализируется строкой “Hello world!” перед ее разворачиванием. Следовательно, на экране печатается “Hello world!”, а не “Default value”.

3. Принудительное разворачивание

Принудительное разворачивание, как следует из названия, - это способ разворачивания значений из дополнительных параметров, при котором мы принудительно разворачиваем значение из переменной, не заботясь о том, имеет ли Optional фактическое значение или значение nil. Мы делаем это, добавляя восклицательный знак (!) После имени Optional переменной. Это небезопасный способ разворачивания, поскольку он приводит к фатальной ошибке, если Optional содержит значение nil. Этот метод следует использовать только в тех случаях, когда мы полностью уверены, что Optional не содержит значения nil.
Давайте посмотрим на это в действии:

var superOptional: String?
superOptional = “Hello world!”
print(superOptional) // prints Optional("Hello world!")
print(superOptional!) // prints Hello world!

В этом примере мы видим, что существует Optional переменная с именем superOptional, которая имеет тип String. Ему присваивается строка “Hello world!”, и она инициализируется. Когда мы печатаем переменную superOptional, не разворачивая ее, печатается Optional(“Hello world!”). Но когда мы печатаем его, добавляя восклицательный знак в конце, печатается Hello world!. Таким образом, на Optional выполняется принудительное развертывание для получения фактического значения. Но что, если мы попытаемся принудительно развернуть и распечатать переменную superOptional без ее инициализации? Или что, если мы попытаемся сделать это, когда значение переменной равно nil? Посмотрим на код:

var superOptional: String? // or var superOptional: String? = nil
print(superOptional!) // force unwrapping

Когда мы выполняем приведенный выше код, наш код дает сбой, и мы получаем следующую ошибку:

Fatal error: Unexpectedly found nil while unwrapping an Optional value

Здесь, поскольку мы использовали принудительное развертывание Optional со значением nil, код потерпел крах. Следовательно, необходимо иметь в виду, что если мы собираемся использовать принудительное развертывание, мы должны быть уверены, что Optional никогда не будет иметь значение nil.

4. Необязательная привязка (если разрешена)

Дополнительное связывание - безопасная альтернатива принудительному разворачиванию. Принудительное развертывание приводит к сбою нашего кода, если Optional, который мы пытаемся развернуть, состоит из значения nil. Но, используя необязательную привязку, мы можем предотвратить это. В этом методе мы переходим к разворачиванию Optional только после того, как убедимся, что он содержит фактическое значение. Для этого мы используем if let. Чтобы пролить свет на это, давайте посмотрим на этот пример:

var superOptional: String?
superOptional = "Hello world!"
if let superValue = superOptional { // optional binding
    print(superValue)
}

Здесь мы проверили, является ли superOptional nil или нет. Если это не nil, значение присваивается superValue. Следовательно, становится безопасным использование superValue, значение которого было получено от superOptional.

5. охранник пусть

guard let - хорошая альтернатива if let для развертывания опций. Если guard let получает nil значение Optional, которое мы пытаемся развернуть, он ожидает, что мы выйдем из функции, цикла или условия, в которых мы его использовали. Однако разница между if let и guard let в том, что мы все еще можем использовать наш развернутый необязательный параметр, даже после кода guard let. Давайте посмотрим на это на примере:

func printName(personName: String?){
    guard let name = personName else {
        print(“No name has been passed.”)
        return
    }
print(“Your name is \(name).”)
}
printName(personName: “John Doe”) // outputs "Your name is John Doe."
printName(personName: nil) // outputs "No name has been passed.

Здесь у нас есть функция printName(personName:). Он принимает необязательный String, называемый personName, и использует guard let, чтобы проверить, является ли personName nil или нет, а затем распечатывает другие выходные данные. В строке с guard let значение personName присваивается name. Если значение personName оказывается равным nil, немедленно выполняется блок else, и управление выходит из функции. В текущем примере сначала печатается “No name has been passed”, а выполнение функции останавливается. Однако, если personName не содержит значения nil, блок else пропускается и печатается имя человека. В данном случае печатается “Your name is John Doe.”.

Не забудьте включить оператор return в блок else элемента guard let, поскольку мы хотим остановить выполнение функции, как только будет обнаружено nil значение.

6. Дополнительная цепочка

Если нам нужно иметь дело с несколькими опциональными опциями одновременно, опциональная цепочка может быть полезным подходом. Предположим, что у нас есть контроллер представления с текстовым полем с именем emailField и имеет тип UITextField?. Допустим, мы также установили @IBOutlet для emailField, что позволяет нам программно устанавливать различные свойства emailField. В этом примере мы рассмотрим его свойство text. Итак, если мы хотим проверить, состоит ли emailField из значения nil или какого-то фактического значения, мы можем использовать как необязательную привязку, так и принудительное развертывание следующим образом:

Использование дополнительной привязки для получения текстового значения emailField:

if let email = emailField, let emailText = email.text {
    print("The email address is: \(emailText)")
}

Использование принудительного развертывания для получения текстового значения emailField:

if emailField != nil && emailField!.text != nil {
    print("The email address is: \(emailField!.text)")
}

Хотя эти подходы можно использовать для разворачивания, становится трудно иметь дело с опциями, когда количество компонентов пользовательского интерфейса увеличивается в контроллере представления. Он превратится в огромное количество условных выражений, скобок и отступов, затрудняющих читаемость кода. Таким образом, в таких условиях может пригодиться необязательная цепочка. Например:

emailField?.text = "[email protected]"

Здесь переменная emailField - это Optional, за которой следует вопросительный знак. На этом этапе в нашем коде могут произойти две вещи: либо переменная emailField не содержит nil значение и доступ к свойству text становится успешным, либо переменная emailField содержит значение nil и доступ к свойству text завершается неудачно.

Когда доступ к свойству text не удается, выполнение этой строки безопасно останавливается без сбоя кода. В данном примере свойство text для emailField не установлено в [email protected], когда emailField равно nil.

Другой пример, показывающий использование необязательной цепочки:

car?.wheel?.airPercent = 50

Вот и все, ребята! Я надеюсь, что эта статья дала вам несколько идей о том, как подружиться со Swift Optionals и использовать их. Я знаю, что вначале они выглядели как наши злейшие враги, но со временем вы поймете, насколько они полезны.

Удачного свифтинга!