Опциональные типы представляют объекты, которые могут иметь, а могут и не иметь значение.
Опциональные типы выступают двойниками базовых типов. Все они имеют в конце вопросительный знак: Int?
,
String?
и т.д. Вопросительный знак как раз указывает, что это опциональный тип.
Например, рассмотрим следующую ситуацию:
let someString = "123" let someNumber = Int(someString)
Здесь инициализатор Int(someString)
преобразует строку someString в число. В данном случае у нас все
нормально, так как строка "123" действительно содержит число 123. Однако, что, если бы переменная someString представляла бы строку "hello"? В этом случае инициализатор не смог бы
преобразовать строку в число. Поэтому инициализатор возвращает не просто объект Int, а Int?,
то есть объект, который может иметь, а может не иметь значения.
По факту, если объект не имеет значения, то ему присваивается специальное значение nil. В коде мы также можем установить явным образом это значение:
var number: Int? = 12 number = nil // теперь переменная number не имеет значения
Значение nil
может применяться только к объектам опциональных типов.
Фактически запись типа Int?
является сокращением от Optional<Int>
. То есть мы также можем определить переменную следующим
образом:
var number: Optional<Int> = 12
Несмотря на то, что в примере выше переменной number присваивается число 12, но фактически переменная будет иметь в качестве
значения Optional(12)
, то есть мы могли бы написать следующим образом:
var number : Optional<Int>= Optional(12) // или так var number2 = Optional(12)
При этом опять же стоит понимать, что Optional<Int>
, это не то же самое, что и
Optional<String>
или Optional<Doublegt;
, например:
var number = Optional(12) number = Optional("12") // Ошибка number представляет тип Optional<Int>, а не Optional<String>
При работе с объектами опциональных типов следует помнить, что они не эквивалентны объектам обычных типов. То есть следующий пример у нас работать не будет:
var a: Int? = 12 var b: Int = 10 var c = a + b // ошибка - разные типы
a и b здесь переменные разных типов, хотя казалось бы обе переменных хранят целые числа. И чтобы полноценно работать с объектами опциональных типов, следует извлечь из них значение. Для извлечения значения используется оператор ! - восклицательный знак после названия объекта опционального типа. Данный оператор еще называют unwrap operator или forced unwrap operator:
var a: Int? = 12 var b: Int = 10 var c = a! + b // с = 22
Другой пример:
var b: Int = 10 var a: Int? = Int("123") b = a! + b print(a!) // 123 print(b) // 133
Swift предоставляет еще один способ получения значения подобных типов, который заключается в использовании типов Optional с неявно получаемым значением (implicitly unwrapped Optional):
var b: Int = 10 var a: Int! = Int("123") b = a + b print(a) // 123 print(b) // 133
Здесь переменная a имеет тип Int!
, а не Int?
. Фактиччески это тот же самый Optional, но
теперь нам явным образом не надо применять оператор ! для получения его значения.
В то же время если переменная a
в примере выше не будет содержать конкретное значение, то программа
опять же выбросит ошибку. Например? в случае var a: Int! = Int("abc")
или var a: Int? = Int("abc")
.
Поэтому перед использованием объектов опциональных типов желательно проверить, что они имеют какие-либо значение.
Для проверки мы можем использовать условную конструкцию if. Ее общая форма:
if var переменная | let константа = опциональное_значение { действия1 } else { действия2 }
Если опциональное_значение не равно nil, то оно присваивается создаваемой переменной (или константе), и выполняются действия1. Иначе выполняются действия2.
Например:
var str: String = "123" var b: Int = 10 if var a = Int(str){ a+=b print(a) } else{ print(b) }
Если выражение Int(str)
(которое возвращает объект Int?) успешно преобразует строку в число, то есть будет иметь значение, то создается переменная a,
которой присваивается полученное значение, и затем выполняется код:
a+=b print(a)
Если же преобразование из строки в число завершится с ошибкой, и выражение Int(str)
возвратит значение nil
, то выполняется код в блоке else:
else{ print(b) }
Но также в данном случае мы могли и по другому проверить на значение nil:
var str: String = "123" var b: Int = 10 var a: Int? = Int(str) if a != nil { a+=b print(a) } else{ print(b) }
Если надо проверить значения нескольких переменных или констант, то все их можно указать в одном выражении if:
let a = Int("123") let b = Int("456") if let aVal = a, let bVal = b{ print(aVal) print(bVal) } else{ print("Error") }
В данном случае выражение if выполняется, если и a, и b не равны nil. Иначе выполняется блок else.
При сравнении объекта Optional с объектом конкретного типа, Swift преобразует объект конкретного типа к типу Optional:
let a: Int? = 10 if a == 10{ print("a is equal to 10") } else{ print("a is not equal to 10") }
И таким образом работают операции == и !=. Однако с операциями <, >, <=, >= все будет несколько иначе. Например, следующий код выдаст ошибку:
let a: Int? = 10 if a > 5{ print("a is greater than 5") }
И в подобных операциях к объекту Optional необходимо применить оператор !:
let a: Int? = 10 if a != nil && a! > 5{ print("a is greater than 5") }
Если сравниваемое значение в конструкции switch представляет объект Optional, то с помощью операции ? мы можем получить и сравнивать его значение при его наличии:
let i = Int("1") switch i { case 1?: print("i is equal to 1") case let n?: print("i is equal to \(n)") case nil: print("i is undefined") }
Оператор ?? позволяет проверить значения объекта Optional на nil. Этот оператор принимает два операнда a ?? 10
. Если первый операнд не равен
nil, то возвращается значение первого операнда. Если первый операнд равен nil, то возвращается второй операнд:
let a = Int("234") let b = a ?? 10 print(b) // 234
В данном случае поскольку константа a не равна nil, то выражение a ?? 10
возвращает значение этой константы, то есть число 234.