Дополнительное объединение в цепочку
Дополнительное объединение в цепочку является процессом для того, чтобы запросить и вызвать свойства, методы, и преобразовывает в нижний индекс на дополнительном, которое могло бы в настоящее время быть nil
. Если дополнительное содержит значение, свойство, метод, или нижний вызов успешно выполняется; если дополнительное nil
, свойство, метод или нижний индекс вызывают возвраты nil
. Многократные запросы могут быть объединены в цепочку вместе, и вся цепочка перестала работать корректно, если какая-либо ссылка в цепочке nil
.
Дополнительное объединение в цепочку как альтернатива принудительному разворачиванию
Вы указываете дополнительное объединение в цепочку путем размещения вопросительного знака (?
) после дополнительного значения, на котором Вы хотите вызвать свойство, метод или нижний индекс, если дополнительное не -nil
. Это очень подобно размещению восклицательного знака (!
) после дополнительного значения для принуждения разворачивания его значения. Основное различие - то, что дополнительное объединение в цепочку перестало работать корректно, когда дополнительное nil
, когда дополнительное, тогда как принудительное разворачивание инициировало ошибку периода выполнения nil
.
Отразить факт, что к дополнительному объединению в цепочку можно обратиться a nil
значение, результатом дополнительного вызова объединения в цепочку всегда является дополнительное значение, даже если свойство, метод или нижний индекс, который Вы запрашиваете, возвращают обязательное значение. Можно использовать это дополнительное возвращаемое значение, чтобы проверить, был ли дополнительный вызов объединения в цепочку успешен (возвращенное дополнительное содержит значение), или не успешно выполнялся вследствие a 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
имеет a residence
значение свойства nil
:
let john = Person()
При попытке получить доступ numberOfRooms
свойство этого лица residence
, путем размещения восклицательного знака после residence
для принуждения разворачивания его значения Вы инициировали ошибку периода выполнения, потому что существует нет residence
значение для разворачивания:
let roomCount = john.residence!.numberOfRooms
// this triggers a runtime error
Код выше успешно выполняется когда john.residence
имеет не -nil
оцените и установит roomCount
к Int
значение, содержащее надлежащее число комнат. Однако этот код всегда инициировал ошибку периода выполнения когда residence
nil
, как проиллюстрировано выше.
Дополнительное объединение в цепочку обеспечивает альтернативный способ получить доступ к значению numberOfRooms
. Для использования дополнительного объединения в цепочку используйте вопросительный знак вместо восклицательного знака:
if let roomCount = john.residence?.numberOfRooms {
println("John's residence has \(roomCount) room(s).")
} else {
println("Unable to retrieve the number of rooms.")
}
// prints "Unable to retrieve the number of rooms."
Это говорит Свифту «цепочке» на дополнительном residence
свойство и получать значение numberOfRooms
если residence
существует.
Поскольку попытка получить доступ numberOfRooms
имеет потенциал для сбоя, дополнительная попытка объединения в цепочку возвращает значение типа Int?
, или “дополнительный Int
”. Когда residence
nil
, как в примере выше, это дополнительное Int
также будет nil
, отразить факт, что это не было возможно к доступу numberOfRooms
.
Обратите внимание на то, что это - истина даже при том, что numberOfRooms
обязательное Int
. Факт, что это запрашивается через дополнительную цепочку, означает что вызов для numberOfRooms
будет всегда возвращаться Int?
вместо Int
.
Можно присвоить a Residence
экземпляр к john.residence
, так, чтобы это больше не имело a nil
значение:
john.residence = Residence()
john.residence
теперь содержит фактическое Residence
экземпляр, а не nil
. При попытке получить доступ numberOfRooms
с тем же дополнительным объединением в цепочку как прежде, это теперь возвратится Int?
это содержит значение по умолчанию numberOfRooms
значение 1
:
if let roomCount = john.residence?.numberOfRooms {
println("John's residence has \(roomCount) room(s).")
} else {
println("Unable to retrieve the number of rooms.")
}
// prints "John's residence has 1 room(s)."
Определение классов модели для дополнительного объединения в цепочку
Можно использовать дополнительное объединение в цепочку с вызовами к свойствам, методам и нижним индексам, которые являются больше чем одним уровнем глубоко. Это позволяет Вам выполнить развертку в подсвойства в сложных моделях взаимосвязанных типов и проверить, возможно ли получить доступ к свойствам, методам и нижним индексам на тех подсвойствах.
Фрагменты кода ниже определяют четыре класса модели для использования в нескольких последующих примерах, включая примеры многоуровневого дополнительного объединения в цепочку. Эти классы подробно останавливаются Person
и Residence
смоделируйте сверху путем добавления a 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() {
println("The number of rooms is \(numberOfRooms)")
}
var address: Address?
}
Поскольку эта версия Residence
хранит массив Room
экземпляры, numberOfRooms
свойство реализовано как вычисленное свойство, не сохраненное свойство. Вычисленный numberOfRooms
свойство просто возвращает значение count
свойство от rooms
массив.
Как ярлык на доступ к 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 buildingName != nil {
return buildingName
} else if buildingNumber != nil {
return buildingNumber
} else {
return nil
}
}
}
Address
класс также обеспечивает вызванный метод buildingIdentifier
, который имеет тип возврата String?
. Этот метод проверяет buildingName
и buildingNumber
свойства и возвраты buildingName
если это имеет значение, или buildingNumber
если это имеет значение, или nil
если никакое свойство не имеет значение.
Доступ к свойствам посредством дополнительного объединения в цепочку
Как продемонстрировано в Дополнительном Объединении в цепочку как Альтернатива Принудительному Разворачиванию, можно использовать дополнительное объединение в цепочку, чтобы получить доступ к свойству на дополнительном значении и проверить, успешен ли тот доступ свойства.
Используйте классы, определенные выше для создания нового Person
экземпляр и попытка получить доступ к numberOfRooms
свойство как прежде:
let john = Person()
if let roomCount = john.residence?.numberOfRooms {
println("John's residence has \(roomCount) room(s).")
} else {
println("Unable to retrieve the number of rooms.")
}
// prints "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
.
Вызывающие методы посредством дополнительного объединения в цепочку
Можно использовать дополнительное объединение в цепочку, чтобы вызвать метод на дополнительном значении и проверить, успешен ли тот вызов метода. Даже если тот метод не определяет возвращаемое значение, можно сделать это.
printNumberOfRooms()
метод на Residence
класс распечатывает текущую стоимость numberOfRooms
. Вот то, как смотрит метод:
func printNumberOfRooms() {
println("The number of rooms is \(numberOfRooms)")
}
Этот метод не указывает тип возврата. Однако функции и методы без типа возврата имеют неявный тип возврата Void
, как описано в Функциях Без Возвращаемых значений. Это означает, что они возвращают значение ()
, или пустой кортеж.
При вызове этого метода на дополнительном значении с дополнительным объединением в цепочку тип возврата метода будет Void?
, нет Void
, потому что возвращаемые значения всегда имеют дополнительный тип, когда вызвано посредством дополнительного объединения в цепочку. Это позволяет Вам использовать if
оператор, чтобы проверить, было ли возможно вызвать printNumberOfRooms()
метод, даже при том, что метод самостоятельно не определяет возвращаемое значение. Сравните возвращаемое значение от printNumberOfRooms
вызовите против nil
видеть, был ли вызов метода успешен:
if john.residence?.printNumberOfRooms() != nil {
println("It was possible to print the number of rooms.")
} else {
println("It was not possible to print the number of rooms.")
}
// prints "It was not possible to print the number of rooms."
При попытке установить свойство посредством дополнительного объединения в цепочку, то же является истиной. Пример выше в Доступе к Свойствам Посредством Дополнительного Объединения в цепочку пытается установить address
значение для john.residence
, даже при том, что residence
свойство nil
. Любая попытка установить свойство посредством дополнительного объединения в цепочку возвращает значение типа Void?
, который позволяет Вам выдержать сравнение с nil
видеть, было ли свойство установлено успешно:
if (john.residence?.address = someAddress) != nil {
println("It was possible to set the address.")
} else {
println("It was not possible to set the address.")
}
// prints "It was not possible to set the address."
Доступ к нижним индексам посредством дополнительного объединения в цепочку
Можно использовать дополнительное объединение в цепочку, чтобы попытаться получить и установить значение от нижнего индекса на дополнительном значении и проверить, успешен ли тот нижний вызов.
Пример ниже пытается получить имя первой комнаты в rooms
массив john.residence
свойство с помощью нижнего индекса, определенного на Residence
класс. Поскольку john.residence
в настоящее время nil
, нижние сбои вызова:
if let firstRoomName = john.residence?[0].name {
println("The first room name is \(firstRoomName).")
} else {
println("Unable to retrieve the first room name.")
}
// prints "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 {
println("The first room name is \(firstRoomName).")
} else {
println("Unable to retrieve the first room name.")
}
// prints "The first room name is Living Room."
Доступ к нижним индексам дополнительного типа
Если нижний индекс возвращает значение дополнительного типа — такого как ключевой нижний индекс Swift Dictionary
введите — помещают вопросительный знак после закрывающей скобки нижнего индекса к цепочке на ее дополнительном возвращаемом значении:
var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]]
testScores["Dave"]?[0] = 91
testScores["Bev"]?[0]++
testScores["Brian"]?[0] = 72
// the "Dave" array is now [91, 82, 84] and the "Bev" array is now [80, 94, 81]
Пример выше определяет вызванный словарь testScores
, который содержит две пары ключ/значение та карта a String
ключ к массиву Int
значения. Пример использует дополнительное объединение в цепочку для установки первого элемента в "Dave"
массив к 91
; постепенно увеличить первый элемент в "Bev"
массив 1
; и попытаться установить первый элемент в массиве для ключа "Brian"
. Первые два вызова успешно выполняются, потому что testScores
словарь содержит ключи для "Dave"
и "Bev"
. Третьи сбои вызова, потому что testScores
словарь не содержит ключ для "Brian"
.
Соединение многократных уровней объединения в цепочку
Вы можете соединить многократные уровни дополнительного объединения в цепочку для развертки к свойствам, методам, и преобразовываете в нижний индекс глубже в модели. Однако многократные уровни дополнительного объединения в цепочку не добавляют больше уровней возможностей к возвращенному значению.
Помещать его иначе:
Если тип, который Вы пытаетесь получить, не будет дополнительным, то это станет дополнительным из-за дополнительного объединения в цепочку.
Если тип, который Вы пытаетесь получить, будет уже дополнительным, то это не станет более дополнительным из-за объединения в цепочку.
Поэтому:
При попытке получить
Int
значение посредством дополнительного объединения в цепочку,Int?
всегда возвращается, независимо от того сколько уровней объединения в цепочку используется.Точно так же, при попытке получить
Int?
значение посредством дополнительного объединения в цепочку,Int?
всегда возвращается, независимо от того сколько уровней объединения в цепочку используется.
Пример ниже пытается получить доступ street
свойство address
свойство residence
свойство john
. Существует два уровня дополнительного объединения в цепочку в использовании здесь к цепочке через residence
и address
свойства, оба из которых имеют дополнительный тип:
if let johnsStreet = john.residence?.address?.street {
println("John's street name is \(johnsStreet).")
} else {
println("Unable to retrieve the address.")
}
// prints "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 {
println("John's street name is \(johnsStreet).")
} else {
println("Unable to retrieve the address.")
}
// prints "John's street name is Laurel Street."
Отметьте использование восклицательного знака во время присвоения экземпляра адреса к john.residence.address
. john.residence
свойство имеет дополнительный тип, и таким образом, необходимо развернуть его фактическое значение с восклицательным знаком прежде, чем получить доступ к местонахождению address
свойство.
Объединение в цепочку на методах с дополнительными возвращаемыми значениями
Предыдущий пример показывает, как получить значение свойства дополнительного типа посредством дополнительного объединения в цепочку. Можно также использовать дополнительное объединение в цепочку для вызова метода, возвращающего значение дополнительного типа, и объединять в цепочку на возвращаемом значении того метода в случае необходимости.
Пример ниже вызывает Address
класс buildingIdentifier()
метод посредством дополнительного объединения в цепочку. Этот метод возвращает значение типа String?
. Как описано выше, окончательный тип возврата этого вызова метода после того, как дополнительное объединение в цепочку также String?
:
if let buildingIdentifier = john.residence?.address?.buildingIdentifier() {
println("John's building identifier is \(buildingIdentifier).")
}
// prints "John's building identifier is The Larches."
Если Вы хотите выполнить далее дополнительное объединение в цепочку на возвращаемом значении этого метода, поместить дополнительный вопросительный знак объединения в цепочку после круглых скобок метода:
if let beginsWithThe =
john.residence?.address?.buildingIdentifier()?.hasPrefix("The") {
if beginsWithThe {
println("John's building identifier begins with \"The\".")
} else {
println("John's building identifier does not begin with \"The\".")
}
}
// prints "John's building identifier begins with "The"."