Опционалы в Swift и опциональные типы данных

Опционалы в Swift могут иметь и не иметь значения. Опционалы являются мощной функцией языка программирования Swift. Любому разработчику важно знать, как их можно использовать.

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

Что такое опционалы?

Опционалы могут либо иметь значение, либо вовсе не иметь значения (в данном случае значение будет равно nil). Вам необходимо извлечь опционалы, чтобы безопасно их использовать.

На базовом уровне код Swift состоит из переменных и функций. Вы используете переменные для хранения информации. Функции выполняют определенные задачи в вашем коде.

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

Предположим, что вы заполняете официальную форму, например, для получения кредита. В форме есть поля для вашего имени, адреса электронной почты и места для удостоверения личности с фотографией вашего лица.

Можно сказать , что поля формы могут быть связаны с переменными, такими как firstname, lastname, email и photo. Текстовые поля являются значениями типа String, а photo имеет тип UIImage.

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

Можно сказать, что поля формы не обязательны. Они могут содержать значение, например ваше имя, или вообще не содержать никакого значения.

Поле, которое не имеет значения имеет специальное обозначение – nil. Это означает «ничего» или «нет значения».

Если переменная не содержит значения, она должна быть объявлена через nil, независимо от типа переменной. Неважно при этом, является ли переменная строкой, целым числом, контроллером представления, кнопкой, изображением или объектом.

В программировании может быть много сценариев, в которых опционалы будут полезными:

  • Преобразование из строки, например “123”, в целое число.
  • Свойство IBOutlet для View Controller, которое является nil перед инициализацией View Controller.
  • Свойства класса, которые будут объявлены как nil до тех пор, пока не будут получены значения из внешнего веб-сервиса или базы данных.

Как использовать опционалы?

Мы можем использовать опционалы в случае, если значение может отсутствовать. Опционал может либо иметь значение, либо его значение может быть nil.

let name: String? = nil
print(name)
// nil

Вот что происходит в приведенном выше примере:

  • В первой строке вы создаете константу name типа String?.
  • Знак вопроса ?, написанный сразу после аннотации типа, указывает, что эта константа является опциональной.
  • Мы назначаете name nil, так эта константа не имеет значения.

А что если мы присвоим конкретное значение для name?

let name: String? = "Артур"
print(name)
// Optional("Артур")

На консоль выводится опциональное значение. Нам нужно извлечь опционал, чтобы получить доступ к его значению.

Вы не можете присвоить nil переменной или константе, которая не является опциональной. Следующий код приведет к ошибке:

let score: Int = nil
print(score)

Переменная или константа могут иметь значение nil только в том случае, если их тип объявлен как опциональный с вопросительным знаком ?

Каждый опционал имеет значение nil, если он не содержит значения:

class Vehicle {
    var wheels: Int = 4
    var name: String?
}
 
let car = Vehicle()
print(car.wheels)
// 4
print(car.name)
// nil
 
car.name = "Maserati"
print(car.name)
// Optional("Maserati")

Вот что происходит:

  • Во-первых, мы объявляем класс Vehicle с двумя свойствами: wheels типа Int со значением по умолчанию 0 и name типа String? без какого-либо значения.
  • Затем мы инициализируем экземпляр класса Vehicle и присваиваем его константе car.
  • Затем мы распечатываем значения свойств wheels и name соответственно. Мы видим, что в консоле отображается 4 и nil.
  • Мы изменяем name на “Maserati”. На данный момент свойство name все еще является опциональным, но теперь оно имеет значение.

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

Вы можете извлечь опционал 4 разными способами:

  1. С помощью принудительного извлечения используя !
  2. Используя опциональное связывание и конструкции if let.
  3. Используя опциональные цепочки.
  4. Используя неявное извлечение опционала с помощью !

Принудительное извлечение опционалов

Рассмотрим принудительное извлечение опционала:

let email: String? = "admin@swiftblog.org"
 
if email != nil {
    print(email!)
}
// admin@swiftblog.org

Сначала мы создаем константу email типа String? и присваиваем ей строку. Константа email является опциональной, так как мы добавляем знак вопроса после ее типа String.

Используя условный оператор if, мы проверяем значение email на nil. Если email не содержит nil, выполняется код в фигурных скобках.

Далее мы добавляем восклицательный знак к email. Это и есть принудительное извлечение опционала.

Вы можете принудительно извлечь любую переменную или константу, добавив к ней восклицательный знак:

print(email!)

Если email будет иметь значение nil, код в фигурных скобках не будет запущен:

let email: String? = nil
 
if email != nil {
    print(email!)
}

Но что если мы не проверим значение email и все равно используем принудильное извлечение?

let email: String? = nil
print(email!)

Когда вы выполните этот код, ваше приложение упадет и предупредит вас о фатальной ошибке :

Fatal error: Unexpectedly found nil while unwrapping an Optional value

Это происходит потому, что вы принудительно извлекаете опциональное значение nil, поэтому никогда не следует этого делать. Всегда сначала проверяйте опциональное значение, прежде чем принудительно его извлекать.

В конечном итоге принудительное извлечение выполняется быстро и приводит к получению более лаконичного кода, однако при этом старадает безопасность вашей программы. Когда вы принудительно извлекаете опционал, не проверив его значение на nil, вы рискуете, что ваше приложение упадет. Используйте принудительное извлечение только в том случае, если вы уверены, что опционал не является nil.

Как использовать опциональное связывание?

Альтернативой принудительному извлечению опционалов является опциональное связывание:

let optionalUsername: String? = "Боб"
 
if let username = optionalUsername {
    print(username)
}
// Боб

Сначала мы создаем константу с именем optionalUsername типа String? и присваиваем ей значение типа String.

Затем мы используем опциональное связывание, чтобы присвоить значение optionalUsername константе username, если ее значение не равно nil.

Вы также можете объявить одно и то же имя переменной дважды в одной и той же области видимости:

let username: String? = "bob42"
 
if let username = username {
    print(username)
}

Самое большое различие между принудительным извлечением и опциональным связыванием состоит в том, что, когда вы используете опциональное связывание, вы не можете случайно извлечь значение nil, что могло бы привести к фатальной ошибке. Это делает ваш код намного безопаснее.

Есть еще несколько вещей, которые вам нужно знать об опциональном связывании.

Вы можете объединить несколько операторов if let и одновременно проверить несколько значений на nil:

if  let first_name = firstnameField.text,
    let last_name = lastnameField.text,
    let email = emailField.text  {
    // 
}

Вы можете использовать опциональное связывание также для функции, которая возвращает опциональное значение:

if let score = Int("42") {
    print(score) 
}

Вы также можете использовать guard let для извлечения опционала:

guard let username = usernameField.text else {
    return
}
 
print("Выполнен вход: \(username)")

В этом случае вы можете использовать созданную константу за пределами тела оператора.

Вы также можете использовать var, а не let для опциональноого связывания. Как и с любыми переменными и константами, вы можете изменить значение переменной, если оно объявлено с помощью var.

Как использовать опциональные цепочки?

Полезным подходом для работы с опционалами является создание опциональной цепочки, особенно если вы работаете с несколькими опциональными значениями одновременно.

Допустим, вы создали ViewController с текстовым полем и именем usernameField типа UITextField?. Вы можете ссылаться на текстовое поле с помощью IBOutlet. Теперь вы можете получить или изменить содержимое текстового поля с помощью свойства text. При этом usernameField и text являются опционалами.

Вы можете сделать это с помощью опционального связывания:

if  let field = usernameField,
    let text  = field.text {
    print("Выполнен вход: \(field.text)")
}

Вы можете использовать принудительное извлечение:

if usernameField != nil && usernameField!.text != nil {
    print("Выполнен вход: \(usernameField!.text!)")
}

Представьте себе, что произойдет, если в вашем ViewController есть 10 компонентов пользовательского интерфейса с опциональными свойствами, к которым вы получаете доступ в нескольких частях вашего кода. Все это быстро превратится в большой беспорядок в бесконечных скобках и отступах.

Чтобы этого избежать, вы можете использовать опциональную цепочку:

usernameField?.text = "Привет!"

В этот момент происходит одна из двух вещей:

  1. Переменная usernameField не имеет значения nil и вызов свойства text завершается успешно.
  2. Переменная usernameField имеет значения nil и вызов свойства text завершается неудачно.

При сбое вызова выполнение этой строки кода останавливается. В приведенном выше примере свойство text usernameField не изменяется, когда usernameField имеет значение nil.

Вы можете использовать опциональную цепочку для нескольких свойств одновременно:

airplane?.wings?.left?.power = "50%"

Вы также можете комбинировать опциональную цепочку с опциональным связыванием:

if let first_name = persons[indexPath.row]?.name?.first {
 
}

Если выражение persons[indexPath.row]?.name?.first возвращает опциональное значение, вы можете присвоить полученный результат константе first_name.

Если вы посмотрите внимательно, вы увидите, что опциональное цепочки и принудительное извлечение опционала являются очень похожими между собой. Сравните следующие строки кода:

textField!.text = "Arthur Dent"
textField?.text = "Arthur Dent"

При этом первая строка, использующая принудительное извлечение, может заставить нашу программу вылететь с ошибкой. Вторая строка сделает наш код безопасным, и если textField будет иметь значение nil, ничего не произойдет.

Неявно извлеченные опционалы

Последним вариантом использования опционалов является неявное извлечение.

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

Следствием этого является то, что вы должны быть уверены, что неявно извлеченный опционал не будет иметь значение nil при обращении к нему. Неявно извлеченный опционал аналогичен разрешению автоматически принудительно извлекать опционал при каждом его использовании.

Неявно извлеченные опционалы могут быть полезны для IBOutlets. IBOutlets будут иметь значение nil, когда View Controller только инициализируется, а затем, когда он полностью загрузится, IBOutlets получат ссылки на соответствующие компоненты пользовательского интерфейса.

Преимущество для разработчика заключается в том, что вам не нужно извлекать IBOutlets каждый раз, когда вы получаете доступ к компонентам пользовательского интерфейса в коде View Controller.

@IBOutlet var usernameField: UITextField!

Мы используем восклицательный знак ! после типа свойства, что обозначает неявно извлеченный опционал.

Читайте также:
Комментарии (2)
Добавить комментарий

Ваш адрес email не будет опубликован.