nil
. Если опционал содержит значение, то свойство, метод или сабскрипт будет вызван успешно; если опционал равен nil
, то свойство, метод или сабскрипт вернут nil
. Множественные запросы могут быть сцеплены вместе, так что провал всего запроса может быть вызван, если любая ссылка в цепочке будет nil
.
nil
, тогда как принудительное извлечение вызывает ошибку времени выполнения, когда опционал равен nil
.
nil
в цепочке (возвращаемое значение содержит nil
).
Int
, вернёт Int?
при доступе через опциональную цепочку.
Person
и Residence
:
class Person {
var residence: Residence?
}
class Residence {
var numberOfRooms = 1
}
Объект Residence
имеет единственное свойство типа Int
с названием numberOfRooms
и значением по-умолчанию 1. Объекты Person
имеют опциональное свойство residence
типа Residence?
.
Person
, то его свойство residence
инициализировано в nil
ввиду его опциональности. В коде ниже john
имеет значение свойства residence nil
:
let john = Person()
numberOfRooms
объект residence
у этого человека, поставив восклицательный знак после residence
для принудительной распаковки его значения, то Вы получите ошибку времени выполнения, так как нет значения в residence
для распаковки:
let roomCount = john.residence!.numberOfRooms
// это вызовет ошибку времени выполненияs
Код выше будет выполнен верно, когда john.residence
имеет не-nil значение и будет устанавливать roomCount
в Int-значение, содержащее подходящее количество комнат. Однако этот код всегда будет вызывать рантайм ошибку, когда residence
имеет nil
, как показано выше.
numberOfRooms
. Чтобы использовать опциональную цепочку, поставьте вопросительный знак вместо восклицательного:
if let roomCount = john.residence?.numberOfRooms {
print("John's residence has \(roomCount) room(s).")
} else {
print("Unable to retrieve the number of rooms.")
}
// Выведет "Unable to retrieve the number of rooms."
Это приказывает Swift связать опциональное свойство residence
в цепочку и вернуть значение numberOfRooms
, если residence
существует.
numberOfRooms
может потенциально провалиться, то опциональная цепочка вернёт значение типа Int?
или "опциональный Int". Когда residence равен nil
, как и в примере выше, тот опциональный Int
будет также равен nil
, чтобы отразить тот факт, что нельзя получить доступ к numberOfRooms
. Опциональный Int
оказывается доступным через опицональное связывание для распаковки целого числа и присвоения неопционального значения переменной roomCount
.
numberOfRooms
- неопциональный Int
. Факт того, что он запрашивается через опциональную цепочку, значит, что вызов numberOfRooms
всегда вернёт Int?
вместо Int
.
Residence
свойству john.residence
, так что больше не будет в нём значения nil
:
john.residence = Residence()
Residence
, а не nil
. Если Вы попытаетесь получить доступ к numberOfRooms
через ту же опциональную цепочку, что и раньше, то она вернёт Int?
, который содержит значение numberOfRooms
по-умолчанию, равное 1:
if let roomCount = john.residence?.numberOfRooms {
print("John's residence has \(roomCount) room(s).")
} else {
print("Unable to retrieve the number of rooms.")
}
// Выведет "John's residence has 1 room(s)."
Person
и Residence
из примеров выше путём добавления классов Room
и Address
со связанными свойствами, методами и сабскриптами.
Person
определён так же, как и ранее:
class Person {
var residence: Residence?
}
Residence
более сложен, чем ранее. На этот раз Residence
определяет переменное свойство с названием rooms
, которое инициализируется пустым массивом типа [Room]
:
class Residence {
var rooms = [Room]()
var numberOfRooms: Int {
return rooms.count
}
subscript(i: Int) -> Room {
get {
return rooms[i]
}
set {
rooms[i] = newValue
}
}
func printNumberOfRooms() {
print("The number of rooms is \(numberOfRooms)")
}
var address: Address?
}
Так как эта версия Residence
хранит массив типа Room
, то его свойство numberOfRooms
реализуется как вычисляемое свойство, а не хранимое. Вычисляемое свойство numberOfRooms
просто возвращает значение свойства count
массива rooms
.
Residence
предоставляет сабскрипт чтения-записи, который предоставляет доступ к комнате по запрашиваемому индексу в массиве rooms
.
Residence
также предоставляет метод с названием printNumberOfRooms
, который просто выводит количество комнат в резиденции.
Residence
определяет опциональное свойство с названием address
и типом Address?
. Класс Address
для этого типа определён ниже.
Room
, используемый для массива rooms
, это простой класс с одним свойством name
и конструктором для задания свойства с подходящим именем комнаты:
class Room {
let name: String
init(name: String) { self.name = name }
}
Address
. Этот класс имеет три опциональных свойства типа String?
. Первые два свойства buildingName
и buildingNumber
- это альтернативные способы идентифицировать отдельное строение в качестве части адреса. Третье свойство, street
, используется в качестве имени улицы для этого адреса:
class Address {
var buildingName: String?
var buildingNumber: String?
var street: String?
func buildingIdentifier() -> String? {
if let buildingNumber = buildingNumber, let street = street {
return "\(buildingNumber) \(street)"
} else if buildingName != nil {
return buildingName
} else {
return nil
}
}
}
Класс Address
также предоставляет метод с названием buildingIdentifier()
, который имеет возвращаемый тип String?
. Этот метод проверяет свойства адреса и возвращает buildingName
, если он имеет значение, или buildingNumber
, сцепленный с street
, если оба имеют значение, или nil
в противном случае.
Person
и для попытки получить numberOfRooms
как раньше:
let john = Person()
if let roomCount = john.residence?.numberOfRooms {
print("John's residence has \(roomCount) room(s).")
} else {
print("Unable to retrieve the number of rooms.")
}
// Выведет "Unable to retrieve the number of rooms."
Ввиду того, что john.residence
равен nil
, эта опциональная цепочка провалится, как и раньше.
let someAddress = Address()
someAddress.buildingNumber = "29"
someAddress.street = "Acacia Road"
john.residence?.address = someAddress
В этом примере попытка установить значение address
у john.residence
провалится, так как john.residence
равен nil
.
someAddress
никогда не будет вычислен, так как доступ к константе не имеет никаких побочных эффектов. Листинг ниже желает то же присваивание, но он использует функцию для создания адреса. Эта функция выводит "Function was called" перед возвращением значения, что позволяет Вам увидеть, вычисляется ли правая часть оператора =.
createAddress()
не вызывается, потому что ничего не выводится.
func createAddress() -> Address {
print("Function was called.")
let someAddress = Address()
someAddress.buildingNumber = "29"
someAddress.street = "Acacia Road"
return someAddress
}
john.residence?.address = createAddress()
printNumberOfRooms()
класса Residence
выводит текущее значение numberOfRooms
. Вот так выглядит тот метод:
func printNumberOfRooms() {
print("The number of rooms is \(numberOfRooms)")
}
Этот метод не определяет возвращаемый тип. Однако функции и методы без возвращаемого значения имеют неявное значение Void
. Это означает, что они возвращают значение () или пустой кортеж.
Void?
, а не Void
, так как возвращаемые значения всегда имеют опциональный тип, когда вызываются через опциональную цепочку. Это позволяет Вам использовать инструкцию if
для проверки того, возможно ли вызвать метод printNumberOfRooms()
, хотя он и не определяет возвращаемое значение. Сравните возвращаемое значение у printNumberOfRooms
c nil
, чтобы увидеть, если метод был успешно вызван:
if john.residence?.printNumberOfRooms() != nil {
print("It was possible to print the number of rooms.")
} else {
print("It was not possible to print the number of rooms.")
}
// Выведет "It was not possible to print the number of rooms."
address
в john.residence
, даже хотя свойство residence
равен nil
. Любая попытка задать свойство через опциональную цепочку возвращает Void?
, что позволяет Вам сравнить его против nil
, чтобы увидеть, если свойство было успешно задано:
if (john.residence?.address = someAddress) != nil {
print("It was possible to set the address.")
} else {
print("It was not possible to set the address.")
}
// Выведет "It was not possible to set the address."
john.residence
, используя сабскрипт, определённый на классе Residence
. Так как john.residence
установлен в nil
, то вызов сабскрипта провалится:
if let firstRoomName = john.residence?[0].name {
print("The first room name is \(firstRoomName).")
} else {
print("Unable to retrieve the first room name.")
}
// Выведет "Unable to retrieve the first room name."
Знак вопроса опциональной цепочки в этом вызове сабскрипта размещён сразу после john.residence
перед квадратными скобками, так как john.residence
- это опциональное значение, на котором происходит вызов опциональной цепочки.
john.residence?[0] = Room(name: "Bathroom")
Эта установка сабскрипта также провалится, так как residence
сейчас равен nil
.
Residence
свойству john.residence
с одним или более объектами типа Room
в массиве rooms
, то Вы сможете использовать сабскрипт Residence
для доступа к актуальным элементам в массиве rooms через опциональную цепочку:
let johnsHouse = Residence()
johnsHouse.rooms.append(Room(name: "Living Room"))
johnsHouse.rooms.append(Room(name: "Kitchen"))
john.residence = johnsHouse
if let firstRoomName = john.residence?[0].name {
print("The first room name is \(firstRoomName).")
} else {
print("Unable to retrieve the first room name.")
}
// Выведет "The first room name is Living Room."
Dictionary
- разместите знак вопроса после закрывающей квадратной скобки сабскрипта для установки цепочки через его опциональное возвращаемое значение:
var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]]
testScores["Dave"]?[0] = 91
testScores["Bev"]?[0] += 1
testScores["Brian"]?[0] = 72
// Массив "Dave" теперь равен [91, 82, 84] и массив "Bev" теперь равен [80, 94, 81]"
Пример выше определяет словарь с названием testScores
c двумя парами ключ-значение, отображающих ключ String
в массив [Int]
. Этот пример использует опциональную последовательность для задания первого элемента "Dave" в массиве в значение 91; для увеличения первого элемента "Bev" в массиве 1; а также попытки установить первый элемент в массиве "Brian". Первые два вызова будут успешными, так как словарь testScores
содержит ключи "Dave" и "Bev". Третий вызов провалится, так как словарь testScores
не содержит ключа "Brian".
street
свойства address
свойства residence
у объекта john
. Здесь используется два уровня опциональной цепочки, чтобы пройти сквозь свойства residence
и address
, оба имеют опциональный тип:
if let johnsStreet = john.residence?.address?.street {
print("John's street name is \(johnsStreet).")
} else {
print("Unable to retrieve the address.")
}
// Выведет "Unable to retrieve the address."
Значение john.residence
теперь содержит корректное значение Residence
. Однако значение john.residence.address
равно nil
. Ввиду этого вызов john.residence?.address?.street
провалится.
street
. Тип этого свойства равен String?
. Возвращаемое значение john.residence?.address?.street
также будет String?
, хотя два уровня опциональной цепочки применяются в дополнение к подразумевающемуся опциональному типу свойства.
Address
в качестве значения для john.residence.address
и значение для свойства адреса street
, то Вы можете получить доступ к значению свойства street
через многоуровневую опциональную цепочку:
let johnsAddress = Address()
johnsAddress.buildingName = "The Larches"
johnsAddress.street = "Laurel Street"
john.residence?.address = johnsAddress
if let johnsStreet = john.residence?.address?.street {
print("John's street name is \(johnsStreet).")
} else {
print("Unable to retrieve the address.")
}
// Выведет "John's street name is Laurel Street."
В этом примере попытка задать свойство address
у объекта john.residence
будет успешной, так как значение john.residence
теперь содержит корректный объект Residence
.
buildingIdentifier()
класса Address
через опциональную цепочку. Этот метод возвращает значение типа String?
. Как описано выше, абсолютный возвращаемый тип вызова этого метода после опциональной цепочки также равен String?
:
if let buildingIdentifier = john.residence?.address?.buildingIdentifier() {
print("John's building identifier is \(buildingIdentifier).")
}
// Выведет "John's building identifier is The Larches."
if let beginsWithThe =
john.residence?.address?.buildingIdentifier()?.hasPrefix("The") {
if beginsWithThe {
print("John's building identifier begins with \"The\".")
} else {
print("John's building identifier does not begin with \"The\".")
}
}
// Выведет "John's building identifier begins with "The"."
В примере выше Вы размещаете знак вопроса после круглых скобок, так как опциональное значение, что Вы получаете - есть возвещаемое значение метода buildingIdentifier()
, а не сам этот метод.