Инициализация
Инициализация является процессом подготовки экземпляра класса, структуры или перечисления для использования. Этот процесс включает установку начального значения для каждого сохраненного свойства на том экземпляре и выполнении любой другой установки или инициализации, требующейся, прежде чем новый экземпляр готов к употреблению.
Вы реализуете этот процесс инициализации путем определения инициализаторов, походящих на специальные методы, которые можно вызвать для создания нового экземпляра определенного типа. В отличие от инициализаторов Objective C, инициализаторы Swift не возвращают значение. Их основная роль должна гарантировать, что новые экземпляры типа правильно инициализируются, прежде чем они будут использоваться впервые.
Экземпляры типов классов могут также реализовать deinitializer, выполняющий любую пользовательскую очистку непосредственно перед тем, как освобожден экземпляр того класса. Для получения дополнительной информации о deinitializers, посмотрите Deinitialization.
Установка начальных значений для сохраненных свойств
Классы и структуры должны установить все свои сохраненные свойства к надлежащему начальному значению к этому времени экземпляр того класса, или структура создается. Сохраненные свойства нельзя оставить в неопределенном состоянии.
Можно установить начальное значение для сохраненного свойства в инициализаторе, или путем присвоения значения свойства по умолчанию как части определения свойства. Эти действия описаны в следующих разделах.
Инициализаторы
Инициализаторы вызывают для создания нового экземпляра определенного типа. В его самой простой форме инициализатор походит на метод экземпляра без параметров, записанное использование init
ключевое слово:
init() {
// perform some initialization here
}
Пример ниже определяет новую вызванную структуру Fahrenheit
сохранить температуры, выраженные в Шкале Фаренгейта. Fahrenheit
структура имеет сохраненное свойство того, temperature
, который имеет тип Double
:
struct Fahrenheit {
var temperature: Double
init() {
temperature = 32.0
}
}
var f = Fahrenheit()
println("The default temperature is \(f.temperature)° Fahrenheit")
// prints "The default temperature is 32.0° Fahrenheit"
Структура определяет единственный инициализатор, init
, без параметров, который инициализирует сохраненную температуру со значением 32.0
(точка замерзания воды, когда выражено в Шкале Фаренгейта).
Значения свойств по умолчанию
Можно установить начальное значение сохраненного свойства из инициализатора, как показано выше. Также укажите значение свойства по умолчанию как часть объявления свойства. Вы указываете значение свойства по умолчанию путем присвоения начального значения свойству, когда это определяется.
Можно записать Fahrenheit
структурируйте сверху в более простой форме путем обеспечения значения по умолчанию для temperature
свойство в точке, которой объявляется свойство:
struct Fahrenheit {
var temperature = 32.0
}
Настройка инициализации
Можно настроить процесс инициализации с входными параметрами и дополнительными типами свойства, или путем присвоения постоянных свойств во время инициализации, как описано в следующих разделах.
Параметры инициализации
Можно обеспечить параметры инициализации как часть определения инициализатора, для определения типов и имен значений, настраивающих процесс инициализации. Параметры инициализации имеют те же возможности и синтаксис как параметры метода и функция.
Следующий пример определяет вызванную структуру Celsius
, который хранит температуры, выраженные в Шкале Цельсия. Celsius
структура реализует два пользовательских вызванные инициализатора init(fromFahrenheit:)
и init(fromKelvin:)
, которые инициализируют новый экземпляр структуры со значением от различной шкалы температур:
struct Celsius {
var temperatureInCelsius: Double
init(fromFahrenheit fahrenheit: Double) {
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
}
init(fromKelvin kelvin: Double) {
temperatureInCelsius = kelvin - 273.15
}
}
let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)
// boilingPointOfWater.temperatureInCelsius is 100.0
let freezingPointOfWater = Celsius(fromKelvin: 273.15)
// freezingPointOfWater.temperatureInCelsius is 0.0
Первый инициализатор имеет единственный параметр инициализации с внешним именем fromFahrenheit
и локальное имя fahrenheit
. Второй инициализатор имеет единственный параметр инициализации с внешним именем fromKelvin
и локальное имя kelvin
. Оба инициализатора преобразовывают свой отдельный аргумент в значение в Шкале Цельсия и хранят это значение в вызванном свойстве temperatureInCelsius
.
Локальные и внешние названия параметра
Как с функцией и параметрами метода, параметры инициализации могут иметь и локальное имя для использования в организации инициализатора и внешнее имя для использования при вызове инициализатора.
Однако инициализаторы не имеют имени функции идентификации перед своими круглыми скобками в пути, функционирующем, и методы делают. Поэтому имена и типы параметров инициализатора играют особенно важную роль в идентификации, какой инициализатор нужно вызвать. Если Вы не обеспечиваете внешнее имя сами, из-за этого Swift обеспечивает автоматическое внешнее имя для каждого параметра в инициализаторе. Это автоматическое внешнее имя совпадает с локальным именем, как будто Вы записали символ хеша перед каждым параметром инициализации.
Следующий пример определяет вызванную структуру Color
, с тремя постоянными вызванными свойствами red
, green
, и blue
. Эти свойства хранят значение между 0.0
и 1.0
указать сумму красного, зеленого цвета, и синий в цвете.
Color
предоставляет инициализатору три соответственно именованных параметра типа Double
для его красных, зеленых, и синих компонентов. Color
также предоставляет второму инициализатору сингл white
параметр, использующийся для обеспечения того же значения для всех трех компонентов цвета.
struct Color {
let red, green, blue: Double
init(red: Double, green: Double, blue: Double) {
self.red = red
self.green = green
self.blue = blue
}
init(white: Double) {
red = white
green = white
blue = white
}
}
Оба инициализатора могут использоваться для создания нового Color
экземпляр, путем обеспечения назвал значения для каждого параметра инициализатора:
let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)
let halfGray = Color(white: 0.5)
Обратите внимание на то, что не возможно вызвать эти инициализаторы, не используя внешние названия параметра. Внешние имена должны всегда использоваться в инициализаторе, если они определяются, и исключение их является ошибкой времени компиляции:
let veryGreen = Color(0.0, 1.0, 0.0)
// this reports a compile-time error - external names are required
Параметры инициализатора без внешних имен
Если Вы не хотите использовать внешнее имя для параметра инициализатора, запишите подчеркивание (_
) вместо явного внешнего имени для того параметра для переопределения поведения по умолчанию.
Вот расширенная версия Celsius
пример от ранее, с дополнительным инициализатором для создания нового Celsius
экземпляр от a Double
значение, которое уже находится в Шкале Цельсия:
struct Celsius {
var temperatureInCelsius: Double
init(fromFahrenheit fahrenheit: Double) {
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
}
init(fromKelvin kelvin: Double) {
temperatureInCelsius = kelvin - 273.15
}
init(_ celsius: Double) {
temperatureInCelsius = celsius
}
}
let bodyTemperature = Celsius(37.0)
// bodyTemperature.temperatureInCelsius is 37.0
Вызов инициализатора Celsius(37.0)
ясно в его намерении без потребности во внешнем названии параметра. Является поэтому надлежащим записать этот инициализатор как init(_ celsius: Double)
так, чтобы это можно было вызвать путем обеспечения ня назван Double
значение.
Дополнительные типы свойства
Если Ваш пользовательский тип имеет сохраненное свойство, которому логически позволяют не иметь “никакого значения” — возможно, потому что его значение не может быть установлено во время инициализации, или потому что позволяется не иметь “никакого значения” в некоторой более поздней точке — объявляют свойство с дополнительным типом. Свойства дополнительного типа автоматически инициализируются со значением nil
, указание, что свойство сознательно предназначается еще, чтобы не иметь “никакого значения” во время инициализации.
Следующий пример определяет вызванный класс SurveyQuestion
, с дополнительным String
свойство вызывают response
:
class SurveyQuestion {
var text: String
var response: String?
init(text: String) {
self.text = text
}
func ask() {
println(text)
}
}
let cheeseQuestion = SurveyQuestion(text: "Do you like cheese?")
cheeseQuestion.ask()
// prints "Do you like cheese?"
cheeseQuestion.response = "Yes, I do like cheese."
Ответ на вопрос об исследовании не может быть известен, пока его не спрашивают, и таким образом, response
свойство объявляется с типом String?
, или “дополнительный String
”. Это автоматически присваивается значение по умолчанию nil
, значение “никакой строки все же”, когда новый экземпляр SurveyQuestion
инициализируется.
Присвоение постоянных свойств во время инициализации
Можно присвоить значение постоянному свойству в любой точке во время инициализации, пока это установлено в определенное значение к тому времени, когда заканчивается инициализация. Как только постоянное свойство присваивается значение, оно не может быть далее изменено.
Можно пересмотреть SurveyQuestion
пример сверху для использования постоянного свойства, а не переменного свойства для text
свойство вопроса, чтобы указать, что вопрос не изменяется один раз экземпляр SurveyQuestion
создается. Даже при том, что text
свойство является теперь константой, оно может все еще быть установлено в инициализаторе класса:
class SurveyQuestion {
let text: String
var response: String?
init(text: String) {
self.text = text
}
func ask() {
println(text)
}
}
let beetsQuestion = SurveyQuestion(text: "How about beets?")
beetsQuestion.ask()
// prints "How about beets?"
beetsQuestion.response = "I also like beets. (But not with cheese.)"
Инициализаторы по умолчанию
Swift обеспечивает инициализатор по умолчанию для любой структуры или базового класса, обеспечивающего значения по умолчанию для всех его свойств и не обеспечивающего сам по крайней мере один инициализатор. Инициализатор по умолчанию просто создает новый экземпляр со всем его набором свойств к их значениям по умолчанию.
Этот пример определяет вызванный класс ShoppingListItem
, который инкапсулирует имя, количество и состояние закупки элемента в списке покупок:
class ShoppingListItem {
var name: String?
var quantity = 1
var purchased = false
}
var item = ShoppingListItem()
Поскольку все свойства ShoppingListItem
класс имеет значения по умолчанию, и потому что это - базовый класс без суперкласса, ShoppingListItem
автоматически получает реализацию инициализатора по умолчанию, создающую новый экземпляр со всем его набором свойств к их значениям по умолчанию. ( name
свойство является дополнительным String
свойство, и таким образом, это автоматически получает значение по умолчанию nil
, даже при том, что это значение не записано в коде.) Пример выше использует инициализатор по умолчанию для ShoppingListItem
класс для создания нового экземпляра класса с синтаксисом инициализатора, записанным как ShoppingListItem()
, и присваивает этот новый экземпляр вызванной переменной item
.
Инициализаторы Memberwise для типов структуры
Типы структуры автоматически получают memberwise инициализатор, если они не определяют ни одного из своих собственных инициализаторов. В отличие от инициализатора по умолчанию, структура получает memberwise инициализатор, даже если она сохранила свойства, не имеющие значений по умолчанию.
memberwise инициализатор является кратким способом инициализировать свойства элемента новых экземпляров структуры. Начальные значения для свойств нового экземпляра могут быть переданы memberwise инициализатору по имени.
Пример ниже определяет вызванную структуру Size
с двумя вызванными свойствами width
и height
. Оба свойства выведены, чтобы иметь тип Double
путем присвоения значения по умолчанию 0.0
.
Size
структура автоматически получает init(width:height:)
инициализатор memberwise, который можно использовать для инициализации нового Size
экземпляр:
struct Size {
var width = 0.0, height = 0.0
}
let twoByTwo = Size(width: 2.0, height: 2.0)
Делегация инициализатора к типам значения
Инициализаторы могут вызвать другие инициализаторы для выполнения части инициализации экземпляра. Этот процесс, известный как делегация инициализатора, избегает копировать код через многократные инициализаторы.
Правила для того, как делегация инициализатора работает, и на то, какие формы делегации позволяются, отличаются для типов значения и типов классов. Типы значения (структуры и перечисления) не поддерживают наследование, и таким образом, их процесс делегации инициализатора относительно прост, потому что они могут только делегировать к другому инициализатору, который они обеспечивают сами. Классы, однако, могут наследоваться от других классов, как описано в Наследовании. Это означает, что классы имеют дополнительные обязанности по обеспечению, что все сохраненные свойства, которые они наследовали, присваиваются подходящее значение во время инициализации. Эта ответственность описана в Наследовании классов и Инициализации ниже.
Для типов значения Вы используете self.init
для обращения к другим инициализаторам от того же значения вводят при записи собственных инициализаторов. Можно только вызвать self.init
из инициализатора.
Обратите внимание на то, что при определении пользовательского инициализатора для типа значения у Вас больше не будет доступа к инициализатору по умолчанию (или memberwise инициализатор, если это будет структура) для того типа. Это ограничение предотвращает ситуацию, в которой дополнительная существенная установка, предоставленная в более сложном инициализаторе, обходится кем-то случайно использование одного из автоматических инициализаторов вместо этого.
Следующий пример определяет пользовательское Rect
структура для представления геометрического прямоугольника. Пример требует двух вызванных опорных конструкций Size
и Point
, оба из которых обеспечивают значения по умолчанию 0.0
для всех их свойств:
struct Size {
var width = 0.0, height = 0.0
}
struct Point {
var x = 0.0, y = 0.0
}
Можно инициализировать Rect
структура ниже одним из трех способов — при помощи ее инициализированного нулем значения по умолчанию origin
и size
значения свойств, путем обеспечения определенной точки источника и размера, или путем обеспечения определенной центральной точки и размера. Эти опции инициализации представлены тремя пользовательскими инициализаторами, которые являются частью Rect
определение структуры:
struct Rect {
var origin = Point()
var size = Size()
init() {}
init(origin: Point, size: Size) {
self.origin = origin
self.size = size
}
init(center: Point, size: Size) {
let originX = center.x - (size.width / 2)
let originY = center.y - (size.height / 2)
self.init(origin: Point(x: originX, y: originY), size: size)
}
}
Первое Rect
инициализатор, init()
, функционально то же как инициализатор по умолчанию, который получила бы структура, если это не имело своих собственных инициализаторов. Этот инициализатор имеет пустую организацию, представленную пустой парой изогнутых фигурных скобок {}
, и не выполняет инициализации. Вызов этого инициализатора возвращает a Rect
экземпляр, чей origin
и size
свойства оба инициализируются со значениями по умолчанию Point(x: 0.0, y: 0.0)
и Size(width: 0.0, height: 0.0)
из их определений свойства:
let basicRect = Rect()
// basicRect's origin is (0.0, 0.0) and its size is (0.0, 0.0)
Второе Rect
инициализатор, init(origin:size:)
, функционально то же как memberwise инициализатор, который получила бы структура, если это не имело своих собственных инициализаторов. Этот инициализатор просто присваивается origin
и size
значения аргументов к надлежащим сохраненным свойствам:
let originRect = Rect(origin: Point(x: 2.0, y: 2.0),
size: Size(width: 5.0, height: 5.0))
// originRect's origin is (2.0, 2.0) and its size is (5.0, 5.0)
Третье Rect
инициализатор, init(center:size:)
, немного более сложно. Это запускается путем вычисления надлежащей точки источника на основе a center
точка и a size
значение. Это тогда вызывает (или делегаты) к init(origin:size:)
инициализатор, хранящий новый источник и значения размера в надлежащих свойствах:
let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
size: Size(width: 3.0, height: 3.0))
// centerRect's origin is (2.5, 2.5) and its size is (3.0, 3.0)
init(center:size:)
инициализатор, возможно, присвоил новые значения origin
и size
к надлежащим свойствам самостоятельно. Однако это более удобно (и расчетная организация в намерении) для init(center:size:)
инициализатор для использования в своих интересах существующего инициализатора, уже обеспечивающего точно ту функциональность.
Наследование классов и инициализация
Все сохраненные свойства класса — включая любые свойства, которые класс наследовал от его суперкласса — должны быть присвоены начальное значение во время инициализации.
Swift определяет два вида инициализаторов для типов классов, чтобы помочь гарантировать, чтобы все сохраненные свойства получили начальное значение. Они известны как определяемые инициализаторы и инициализаторы удобства.
Определяемые инициализаторы и инициализаторы удобства
Определяемые инициализаторы являются основными инициализаторами для класса. Определяемый инициализатор полностью инициализирует все свойства, представленные тем классом, и вызывает надлежащий инициализатор суперкласса для продолжения процесса инициализации цепочка суперкласса.
Классы имеют тенденцию иметь очень немного определяемых инициализаторов, и классу довольно свойственно иметь только один. Определяемые инициализаторы являются точками «трубы», через которые инициализация имеет место, и через который процесс инициализации продолжает цепочку суперкласса.
Каждый класс должен иметь по крайней мере один определяемый инициализатор. В некоторых случаях это требование удовлетворено путем наследования один или несколько определяемые инициализаторы от суперкласса, как описано в Автоматическом Наследовании Инициализатора ниже.
Инициализаторы удобства вторичны, поддерживая инициализаторы для класса. Можно определить инициализатор удобства для вызова определяемого инициализатора от того же класса как инициализатор удобства с частью набора параметров определяемого инициализатора к значениям по умолчанию. Можно также определить инициализатор удобства для создания экземпляра того класса для определенного типа варианта использования или входного значения.
Вы не должны обеспечивать инициализаторы удобства, если Ваш класс не требует их. Создайте инициализаторы удобства каждый раз, когда ярлык на общий образец инициализации сэкономит время или сделает инициализацию расчетной организации класса в намерении.
Синтаксис для определяемого и инициализаторов удобства
Определяемые инициализаторы для классов записаны таким же образом как простые инициализаторы для типов значения:
init(parameters) {
statements
}
Инициализаторы удобства записаны в том же стиле, но с convenience
модификатор поместил перед init
ключевое слово, разделенное пространством:
convenience init(parameters) {
statements
}
Делегация инициализатора к типам классов
Для упрощения отношений между определяемым и инициализаторами удобства Swift применяет соблюдающий трех правил для вызовов делегации между инициализаторами:
- Правило 1
Определяемый инициализатор должен вызвать определяемый инициализатор от своего непосредственного суперкласса.
- Правило 2
Инициализатор удобства должен вызвать другой инициализатор от того же класса.
- Правило 3
Инициализатор удобства должен в конечном счете вызвать определяемый инициализатор.
Простой способ помнить это:
Определяемые инициализаторы должны всегда делегировать.
Инициализаторы удобства должны всегда делегировать через.
Эти правила проиллюстрированы в числе ниже:
Здесь, суперкласс имеет единственный определяемый инициализатор и два инициализатора удобства. Один инициализатор удобства вызывает другой инициализатор удобства, поочередно вызывающий единственный определяемый инициализатор. Это удовлетворяет правила 2 и 3 сверху. Суперкласс самостоятельно не имеет дальнейшего суперкласса, и таким образом, не применяется правило 1.
Подкласс в этом числе имеет два определяемых инициализатора и один инициализатор удобства. Инициализатор удобства должен вызвать один из двух определяемых инициализаторов, потому что это может только вызвать другой инициализатор от того же класса. Это удовлетворяет правила 2 и 3 сверху. Оба определяемых инициализатора должны вызвать единственный определяемый инициализатор от суперкласса, для удовлетворения правила 1 сверху.
Число ниже шоу более сложная иерархия классов для четырех классов. Это иллюстрирует, как определяемые инициализаторы в этой иерархии действуют как точки «трубы» для инициализации класса, упрощая взаимосвязи среди классов в цепочке:
Двухфазная инициализация
Инициализация класса в Swift является двухфазным процессом. В первой фазе каждое сохраненное свойство присваивается начальное значение классом, представившим его. Как только начальное состояние для каждого сохраненного свойства было определено, вторая фаза начинается, и каждому классу дают возможность настроить ее сохраненные свойства далее, прежде чем новый экземпляр будут считать готовым к употреблению.
Использование двухфазного процесса инициализации делает инициализацию безопасной при тихом предоставлении полной гибкости каждому классу в иерархии классов. Двухфазная инициализация препятствует тому, чтобы значения свойств были получены доступ, прежде чем они будут инициализированы, и препятствует тому, чтобы значения свойств были неожиданно установлены в различное значение другим инициализатором.
Компилятор Swift выполняет четыре полезных проверки безопасности, чтобы удостовериться, что двухфазная инициализация завершается без ошибки:
- Проверка безопасности 1
Определяемый инициализатор должен гарантировать, что все свойства, представленные его классом, инициализируются, прежде чем он делегирует до инициализатора суперкласса.
Как упомянуто выше, память для объекта только считают полностью инициализированной, как только известно начальное состояние всех его сохраненных свойств. Для этого правила, которое будет удовлетворено, определяемый инициализатор должен удостовериться, что все его собственные свойства инициализируются, прежде чем это передаст цепочку.
- Проверка безопасности 2
Определяемый инициализатор должен делегировать до инициализатора суперкласса прежде, чем присвоить значение унаследованному свойству. Если это не сделает, то новое значение, которое присваивает определяемый инициализатор, будет перезаписано суперклассом как часть его собственной инициализации.
- Проверка безопасности 3
Инициализатор удобства должен делегировать к другому инициализатору прежде, чем присвоить значение любому свойству (включая свойства, определенные тем же классом). Если это не сделает, то новое значение, которое присваивает инициализатор удобства, будет перезаписано определяемым инициализатором его собственного класса.
- Проверка безопасности 4
Инициализатор не может вызвать методы экземпляра, считать значения любых свойств экземпляра или относиться к
self
как значение, пока первая фаза инициализации не завершена.
Экземпляр класса не полностью допустим, пока не заканчивается первая фаза. К свойствам можно только получить доступ, и методы можно только вызвать, как только экземпляр класса, как известно, допустим в конце первой фазы.
Вот то, как двухфазная инициализация теряет значение, на основе этих четырех проверок безопасности выше:
Фаза 1
Инициализатор определяемого или удобства вызывают на классе.
Память для нового экземпляра того класса выделяется. Память еще не инициализируется.
Определяемый инициализатор для того класса подтверждает, что все сохраненные свойства, представленные тем классом, имеют значение. Память для этих сохраненных свойств теперь инициализируется.
Определяемый инициализатор передает к инициализатору суперкласса для выполнения той же задачи для ее собственных сохраненных свойств.
Это продолжает цепочку наследования классов, пока не достигнута вершина цепочки.
Как только вершина цепочки достигнута, и заключительный класс в цепочке гарантировал, чтобы все ее сохраненные свойства имели значение, память экземпляра считается полностью инициализированной, и фаза 1 завершена.
Фаза 2
Работа отступает от вершины цепочки, каждый определяемый инициализатор в цепочке имеет опцию настроить экземпляр далее. Инициализаторы теперь в состоянии получить доступ
self
и может изменить его свойства, вызвать его методы экземпляра, и т.д.Наконец, любые инициализаторы удобства в цепочке имеют опцию настроить экземпляр и работать с
self
.
Вот то, как фаза 1 ищет требование инициализации гипотетического подкласса и суперкласса:
В этом примере инициализация начинается с вызова к инициализатору удобства на подклассе. Этот инициализатор удобства еще не может изменить свойства. Это делегирует через к определяемому инициализатору от того же класса.
Определяемый инициализатор удостоверяется, что все свойства подкласса имеют значение согласно проверке безопасности 1. Это тогда вызывает определяемый инициализатор на своем суперклассе для продолжения инициализации цепочка.
Определяемый инициализатор суперкласса удостоверяется, что все свойства суперкласса имеют значение. Нет никаких дальнейших суперклассов для инициализации, и таким образом, не необходима никакая дальнейшая делегация.
Как только все свойства суперкласса имеют начальное значение, его память считают полностью инициализированной, и Фаза 1 завершена.
Вот то, как фаза 2 ищет тот же вызов инициализации:
Определяемый инициализатор суперкласса теперь имеет возможность настроить экземпляр далее (несмотря на то, что это не имеет к).
Как только определяемый инициализатор суперкласса закончен, определяемый инициализатор подкласса может выполнить дополнительную настройку (несмотря на то, что снова, это не имеет к).
Наконец, как только определяемый инициализатор подкласса закончен, инициализатор удобства, который первоначально вызвали, может выполнить дополнительную настройку.
Наследование инициализатора и переопределение
В отличие от подклассов в Objective C, подклассы Swift не наследовали свои инициализаторы суперкласса по умолчанию. Подход Swift предотвращает ситуацию, в которой простой инициализатор от суперкласса наследован более специализированным подклассом и используется для создания нового экземпляра полностью или правильно не инициализирующегося подкласса.
Если Вы хотите, чтобы пользовательский подкласс представил один или больше тех же инициализаторов как его суперкласс, можно обеспечить пользовательскую реализацию тех инициализаторов в подклассе.
Когда Вы пишете инициализатор подкласса, соответствующий суперклассу определяемый инициализатор, Вы эффективно обеспечиваете переопределение того определяемого инициализатора. Поэтому необходимо записать override
модификатор перед определением инициализатора подкласса. Это - истина даже при переопределении автоматически предоставленного инициализатора по умолчанию, как описано в Инициализаторах По умолчанию.
Как с переопределенным свойством, методом или нижним индексом, присутствием override
модификатор предлагает Swift проверять, что суперкласс имеет соответствие определяемый инициализатор, который будет переопределен и проверяет это, параметры для Вашего переопределяющего инициализатора были указаны, как предназначено.
С другой стороны, если Вы пишете инициализатор подкласса, соответствующий инициализатор удобства суперкласса, тот инициализатор удобства суперкласса никогда не может вызывать непосредственно Ваш подкласс согласно правилам, описанным выше в Делегации Инициализатора к Типам классов. Поэтому Ваш подкласс (строго говоря) не обеспечивает переопределение инициализатора суперкласса. В результате Вы не пишете override
модификатор при обеспечении соответствующей реализации инициализатора удобства суперкласса.
Пример ниже определяет вызванный базовый класс Vehicle
. Этот базовый класс объявляет сохраненное вызванное свойство numberOfWheels
, со значением по умолчанию Int
значение 0
. numberOfWheels
свойство используется вычисленным вызванным свойством description
создать a String
описание характеристик механизма:
class Vehicle {
var numberOfWheels = 0
var description: String {
return "\(numberOfWheels) wheel(s)"
}
}
Vehicle
класс обеспечивает значение по умолчанию для своего единственного сохраненного свойства и не обеспечивает пользовательских инициализаторов самого. В результате это автоматически получает инициализатор по умолчанию, как описано в Инициализаторах По умолчанию. Инициализатор по умолчанию (когда доступный) всегда является определяемым инициализатором для класса и может использоваться для создания нового Vehicle
экземпляр с a numberOfWheels
из 0
:
let vehicle = Vehicle()
println("Vehicle: \(vehicle.description)")
// Vehicle: 0 wheel(s)
Следующий пример определяет подкласс Vehicle
вызванный Bicycle
:
class Bicycle: Vehicle {
override init() {
super.init()
numberOfWheels = 2
}
}
Bicycle
подкласс определяет пользовательский определяемый инициализатор, init()
. Этот определяемый инициализатор соответствует определяемый инициализатор от суперкласса Bicycle
, и так Bicycle
версия этого инициализатора отмечена с override
модификатор.
init()
инициализатор для Bicycle
запускается путем вызова super.init()
, который вызывает инициализатор по умолчанию для Bicycle
суперкласс класса, Vehicle
. Это гарантирует что numberOfWheels
унаследованное свойство инициализируется Vehicle
прежде Bicycle
имеет возможность изменить свойство. После вызова super.init()
, исходное значение numberOfWheels
заменяется новым значением 2
.
Если Вы создаете экземпляр Bicycle
, можно вызвать его наследованный description
вычисленное свойство, чтобы видеть, как numberOfWheels
свойство было обновлено:
let bicycle = Bicycle()
println("Bicycle: \(bicycle.description)")
// Bicycle: 2 wheel(s)
Автоматическое наследование инициализатора
Как упомянуто выше, подклассы не наследовали свои инициализаторы суперкласса по умолчанию. Если определенные условия соблюдены, Однако инициализаторы суперкласса автоматически наследованы. На практике это означает, что Вы не должны писать переопределения инициализатора во многих общих сценариях и можете наследовать Ваши инициализаторы суперкласса с минимальным усилием каждый раз, когда безопасно сделать так.
Предположение, что Вы обеспечиваете значения по умолчанию для любых новых свойств, которые Вы представляете в подклассе, соблюдающий двух правил, применяется:
- Правило 1
Если Ваш подкласс не определяет определяемых инициализаторов, он автоматически наследовал весь свой суперкласс определяемые инициализаторы.
- Правило 2
Если Ваш подкласс обеспечивает реализацию всего ее суперкласса определяемые инициализаторы — или путем наследования их согласно правилу 1, или путем обеспечения пользовательской реализации как части ее определения — тогда это автоматически наследовало все инициализаторы удобства суперкласса.
Даже если Ваш подкласс добавляет дальнейшие инициализаторы удобства, эти правила применяются.
Определяемый и инициализаторы удобства в действии
Следующий пример показывает определяемые инициализаторы, инициализаторы удобства и автоматическое наследование инициализатора в действии. Этот пример определяет иерархию трех вызванных классов Food
, RecipeIngredient
, и ShoppingListItem
, и демонстрирует, как взаимодействуют их инициализаторы.
Базовый класс в иерархии вызывают Food
, который является простым классом для инкапсуляции имени пищевых продуктов. Food
класс представляет сингл String
свойство вызывают name
и обеспечивает два инициализатора для создания Food
экземпляры:
class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[Unnamed]")
}
}
Число ниже шоу цепочка инициализатора для Food
класс:
Классы не имеют значения по умолчанию memberwise инициализатором, и таким образом, Food
класс обеспечивает определяемый инициализатор, берущий вызванный отдельный аргумент name
. Этот инициализатор может использоваться для создания нового Food
экземпляр с собственным именем:
let namedMeat = Food(name: "Bacon")
// namedMeat's name is "Bacon"
init(name: String)
инициализатор от Food
класс предоставлен как определяемый инициализатор, потому что он гарантирует что все сохраненные свойства нового Food
экземпляр полностью инициализируется. Food
класс не имеет суперкласса, и таким образом, init(name: String)
инициализатор не должен вызывать super.init()
завершать его инициализацию.
Food
класс также обеспечивает инициализатор удобства, init()
, без параметров. init()
инициализатор обеспечивает имя заполнителя по умолчанию для новой еды путем делегирования через к Food
класс init(name: String)
с a name
значение [Unnamed]
:
let mysteryMeat = Food()
// mysteryMeat's name is "[Unnamed]"
Второй класс в иерархии является подклассом Food
вызванный RecipeIngredient
. RecipeIngredient
класс моделирует компонент в рецепте кулинарии. Это представляет Int
свойство вызывают quantity
(в дополнение к name
свойство это наследовалось от Food
) и определяет два инициализатора для создания RecipeIngredient
экземпляры:
class RecipeIngredient: Food {
var quantity: Int
init(name: String, quantity: Int) {
self.quantity = quantity
super.init(name: name)
}
override convenience init(name: String) {
self.init(name: name, quantity: 1)
}
}
Число ниже шоу цепочка инициализатора для RecipeIngredient
класс:
RecipeIngredient
класс имеет единственный определяемый инициализатор, init(name: String, quantity: Int)
, который может использоваться для заполнения всех свойств нового RecipeIngredient
экземпляр. Этот инициализатор запускается путем присвоения переданного quantity
параметр quantity
свойство, которое является единственным новым свойством, представленным RecipeIngredient
. После выполнения так, инициализатор делегирует до init(name: String)
инициализатор Food
класс. Этот процесс удовлетворяет проверку безопасности 1 от Двухфазной Инициализации выше.
RecipeIngredient
также определяет инициализатор удобства, init(name: String)
, который используется для создания a RecipeIngredient
экземпляр по имени один. Этот инициализатор удобства принимает количество 1
для любого RecipeIngredient
экземпляр, создающийся без явного количества. Определение этого инициализатора удобства делает RecipeIngredient
экземпляры, более быстрые и более удобные для создания, и, избегают дублирования кода при создании нескольких единственных количеств RecipeIngredient
экземпляры. Этот инициализатор удобства просто делегирует через к определяемому инициализатору класса, передающему в a quantity
значение 1
.
init(name: String)
инициализатор удобства, предоставленный RecipeIngredient
берет те же параметры в качестве init(name: String)
определяемый инициализатор от Food
. Поскольку этот инициализатор удобства переопределяет определяемый инициализатор от своего суперкласса, он должен быть отмечен с override
модификатор (как описано в Наследовании Инициализатора и Переопределяющий).
Даже при том, что RecipeIngredient
обеспечивает init(name: String)
инициализатор как инициализатор удобства, RecipeIngredient
тем не менее, обеспечил реализацию всех определяемых инициализаторов ее суперкласса. Поэтому RecipeIngredient
автоматически наследовал все инициализаторы удобства его суперкласса также.
В этом примере, суперклассе для RecipeIngredient
Food
, который имеет единственный вызванный инициализатор удобства init()
. Этот инициализатор поэтому наследован RecipeIngredient
. Наследованная версия init()
функции точно таким же образом как Food
версия, за исключением того, что это делегирует к RecipeIngredient
версия init(name: String)
вместо Food
версия.
Все три из этих инициализаторов могут использоваться для создания новый RecipeIngredient
экземпляры:
let oneMysteryItem = RecipeIngredient()
let oneBacon = RecipeIngredient(name: "Bacon")
let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6)
Третий и заключительный класс в иерархии является подклассом RecipeIngredient
вызванный ShoppingListItem
. ShoppingListItem
класс моделирует компонент рецепта, как это появляется в списке покупок.
Каждый элемент в списке покупок начинается, как «не куплено». Представлять этот факт, ShoppingListItem
представляет вызванное булево свойство purchased
, со значением по умолчанию false
. ShoppingListItem
также добавляет вычисленный description
свойство, предоставляющее текстовое описание a ShoppingListItem
экземпляр:
class ShoppingListItem: RecipeIngredient {
var purchased = false
var description: String {
var output = "\(quantity) x \(name)"
output += purchased ? " ✔" : " ✘"
return output
}
}
Поскольку это обеспечивает значение по умолчанию для всех свойств, это представляет и не определяет инициализаторов само, ShoppingListItem
автоматически наследовал все определяемые и инициализаторы удобства от его суперкласса.
Число ниже шоу полная цепочка инициализатора для всех трех классов:
Можно использовать все три из наследованных инициализаторов для создания нового ShoppingListItem
экземпляр:
var breakfastList = [
ShoppingListItem(),
ShoppingListItem(name: "Bacon"),
ShoppingListItem(name: "Eggs", quantity: 6),
]
breakfastList[0].name = "Orange juice"
breakfastList[0].purchased = true
for item in breakfastList {
println(item.description)
}
// 1 x Orange juice ✔
// 1 x Bacon ✘
// 6 x Eggs ✘
Здесь, новый массив вызывают breakfastList
создается из литерала массивов, содержащего три новых ShoppingListItem
экземпляры. Тип массива выведен, чтобы быть [ShoppingListItem]
. После того, как массив создается, имя ShoppingListItem
в начале массива изменяется от "[Unnamed]"
к "Orange juice"
и это отмечено как купленный. Печать описания каждого элемента в массиве показывает, что их состояния по умолчанию были установлены как ожидалось.
Инициализаторы Failable
Иногда полезно определить класс, структуру или перечисление, для которого может перестать работать инициализация. Этот отказ мог бы быть инициирован недопустимыми значениями параметров инициализации, отсутствием требуемого внешнего ресурса или некоторым другим условием, препятствующим тому, чтобы успешно выполнилась инициализация.
Для разрешения с условиями инициализации, которые могут перестать работать определите один или несколько failable инициализаторы как часть класса, структуры или определения перечисления. Вы пишете failable инициализатор путем размещения вопросительного знака после init
ключевое слово (init?
).
failable инициализатор создает дополнительную ценность типа, который он инициализирует. Вы пишете return nil
в failable инициализаторе для указания точки, в которой может быть инициирован отказ в инициализации.
Пример ниже определяет вызванную структуру Animal
, с константой String
свойство вызывают species
. Animal
структура также определяет failable инициализатор с помощью единственного вызванного параметра species
. Этот инициализатор проверяет если species
значение, переданное инициализатору, является пустой строкой. Если пустая строка найдена, отказ в инициализации инициирован. Иначе, species
значение свойства установлено, и инициализация успешно выполняется:
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
}
Можно использовать этот failable инициализатор, чтобы попытаться инициализировать новое Animal
экземпляр и проверять, успешно выполнилась ли инициализация:
let someCreature = Animal(species: "Giraffe")
// someCreature is of type Animal?, not Animal
if let giraffe = someCreature {
println("An animal was initialized with a species of \(giraffe.species)")
}
// prints "An animal was initialized with a species of Giraffe"
Если Вы передаете значение пустой строки failable инициализатору species
параметр, инициализатор инициировал отказ в инициализации:
let anonymousCreature = Animal(species: "")
// anonymousCreature is of type Animal?, not Animal
if anonymousCreature == nil {
println("The anonymous creature could not be initialized")
}
// prints "The anonymous creature could not be initialized"
Инициализаторы Failable для перечислений
Можно использовать failable инициализатор для выбора соответствующего элемента перечисления на основе одного или более параметров. Если предоставленные параметры не соответствуют соответствующему элементу перечисления, инициализатор может тогда перестать работать.
Пример ниже определяет вызванное перечисление TemperatureUnit
, с тремя возможными состояниями (Kelvin
, Celsius
, и Fahrenheit
). failable инициализатор используется для нахождения соответствующего элемента перечисления для a Character
значение, представляющее температурный символ:
enum TemperatureUnit {
case Kelvin, Celsius, Fahrenheit
init?(symbol: Character) {
switch symbol {
case "K":
self = .Kelvin
case "C":
self = .Celsius
case "F":
self = .Fahrenheit
default:
return nil
}
}
}
Если параметр не соответствует одно из этих состояний, можно использовать этот failable инициализатор, чтобы выбрать соответствующий элемент перечисления для трех возможных состояний и заставить инициализацию перестать работать:
let fahrenheitUnit = TemperatureUnit(symbol: "F")
if fahrenheitUnit != nil {
println("This is a defined temperature unit, so initialization succeeded.")
}
// prints "This is a defined temperature unit, so initialization succeeded."
let unknownUnit = TemperatureUnit(symbol: "X")
if unknownUnit == nil {
println("This is not a defined temperature unit, so initialization failed.")
}
// prints "This is not a defined temperature unit, so initialization failed."
Инициализаторы Failable для перечислений с необработанными значениями
Перечисления с необработанными значениями автоматически получают failable инициализатор, init?(rawValue:)
, это берет вызванный параметр rawValue
из надлежащего типа необработанного значения и выбирает соответствующий элемент перечисления, если Вы найдены или инициировали отказ в инициализации, если не существует никакое совпадающее значение.
Можно переписать TemperatureUnit
пример сверху для использования необработанных значений типа Character
и использовать в своих интересах init?(rawValue:)
инициализатор:
enum TemperatureUnit: Character {
case Kelvin = "K", Celsius = "C", Fahrenheit = "F"
}
let fahrenheitUnit = TemperatureUnit(rawValue: "F")
if fahrenheitUnit != nil {
println("This is a defined temperature unit, so initialization succeeded.")
}
// prints "This is a defined temperature unit, so initialization succeeded."
let unknownUnit = TemperatureUnit(rawValue: "X")
if unknownUnit == nil {
println("This is not a defined temperature unit, so initialization failed.")
}
// prints "This is not a defined temperature unit, so initialization failed."
Инициализаторы Failable для классов
failable инициализатор для типа значения (т.е. структура или перечисление) может инициировать отказ в инициализации в любой точке в ее реализации инициализатора. В Animal
пример структуры выше, инициализатор инициировал отказ в инициализации в, очень запускаются его реализации, перед species
свойство было установлено.
Для классов, однако, failable инициализатор может инициировать отказ в инициализации только после того, как все сохраненные свойства, представленные тем классом, были установлены в начальное значение, и любая делегация инициализатора имела место.
Пример ниже шоу, как можно использовать неявно развернутое дополнительное свойство для удовлетворения этого требования в failable инициализаторе класса:
class Product {
let name: String!
init?(name: String) {
self.name = name
if name.isEmpty { return nil }
}
}
Product
класс, определенный выше, очень подобен Animal
структура, замеченная ранее. Product
класс имеет константу name
свойство, которому нельзя позволить принять значение пустой строки. Осуществлять это требование, Product
класс использует failable инициализатор, чтобы гарантировать, что значение свойства непусто прежде, чем позволить инициализации успешно выполняться.
Однако Product
класс, не структура. Это означает это в отличие от этого Animal
, любой failable инициализатор для Product
класс должен обеспечить начальное значение для name
свойство прежде, чем инициировать отказ в инициализации.
В примере выше, name
свойство Product
класс определяется как наличие неявно развернутого дополнительного строкового типа (String!
). Поскольку это имеет дополнительный тип, это означает что name
свойство имеет значение по умолчанию nil
прежде чем это будет присвоено определенное значение во время инициализации. Это значение по умолчанию nil
поочередно средние значения, что все свойства, представленные Product
класс имеет допустимое начальное значение. В результате failable инициализатор для Product
может инициировать отказ в инициализации в начале инициализатора, если он передается пустая строка, прежде, чем присвоить определенное значение name
свойство в инициализаторе.
Поскольку name
свойство является константой, можно быть уверены, что оно будет всегда содержать не -nil
оцените, если успешно выполняется инициализация. Даже при том, что это определяется с помощью неявно развернутого дополнительного типа, можно всегда получать доступ к его неявно развернутому значению с уверенностью, не будучи должен проверить на значение nil
:
if let bowTie = Product(name: "bow tie") {
// no need to check if bowTie.name == nil
println("The product's name is \(bowTie.name)")
}
// prints "The product's name is bow tie"
Распространение отказа в инициализации
failable инициализатор класса, структуры или перечисления может делегировать через к другому failable инициализатору от того же класса, структуры или перечисления. Точно так же подкласс failable инициализатор может делегировать до суперкласса failable инициализатор.
В любом случае, если Вы делегируете к другому инициализатору, заставляющему инициализацию перестать работать, весь процесс инициализации сразу перестал работать, и никакой дальнейший код инициализации не выполняется.
Пример ниже определяет подкласс Product
вызванный CartItem
. CartItem
класс моделирует элемент в корзине онлайн-шоппинга. CartItem
представляет сохраненное постоянное вызванное свойство quantity
и гарантирует, что это свойство всегда имеет значение, по крайней мере, 1
:
class CartItem: Product {
let quantity: Int!
init?(name: String, quantity: Int) {
self.quantity = quantity
super.init(name: name)
if quantity < 1 { return nil }
}
}
quantity
свойство имеет неявно развернутый целый тип (Int!
). Как с name
свойство Product
класс, это означает что quantity
свойство имеет значение по умолчанию nil
прежде чем это будет присвоено определенное значение во время инициализации.
failable инициализатор для CartItem
запускается путем делегирования до init(name:)
инициализатор от его суперкласса, Product
. Это удовлетворяет требование, чтобы failable инициализатор всегда выполнял делегацию инициализатора прежде, чем инициировать отказ в инициализации.
Если инициализация суперкласса перестала работать из-за пустого name
значение, весь процесс инициализации сразу перестал работать, и никакой дальнейший код инициализации не выполняется. Если инициализация суперкласса успешно выполняется, CartItem
инициализатор проверяет это, он получил a quantity
значение 1
или больше.
Если Вы создаете a CartItem
экземпляр с непустым названием и количеством 1
или больше, инициализация успешно выполняется:
if let twoSocks = CartItem(name: "sock", quantity: 2) {
println("Item: \(twoSocks.name), quantity: \(twoSocks.quantity)")
}
// prints "Item: sock, quantity: 2"
При попытке создать a CartItem
экземпляр с a quantity
значение 0
, CartItem
инициализатор заставляет инициализацию перестать работать:
if let zeroShirts = CartItem(name: "shirt", quantity: 0) {
println("Item: \(zeroShirts.name), quantity: \(zeroShirts.quantity)")
} else {
println("Unable to initialize zero shirts")
}
// prints "Unable to initialize zero shirts"
Точно так же, при попытке создать a CartItem
экземпляр с пустым name
значение, суперкласс Product
инициализатор заставляет инициализацию перестать работать:
if let oneUnnamed = CartItem(name: "", quantity: 1) {
println("Item: \(oneUnnamed.name), quantity: \(oneUnnamed.quantity)")
} else {
println("Unable to initialize one unnamed product")
}
// prints "Unable to initialize one unnamed product"
Переопределение инициализатора Failable
Можно переопределить суперкласс failable инициализатор в подклассе, точно так же, как любой другой инициализатор. Также можно переопределить суперкласс failable инициализатор с подклассом non-failable инициализатор. Это позволяет Вам определить подкласс, для которого не может перестать работать инициализация, даже при том, что инициализации суперкласса позволяют перестать работать.
Обратите внимание на то, что при переопределении failable инициализатора суперкласса с nonfailable инициализатором подкласса инициализатор подкласса не может делегировать до инициализатора суперкласса. nonfailable инициализатор никогда не может делегировать к failable инициализатору.
Пример ниже определяет вызванный класс Document
. Этот класс моделирует документ, который может быть инициализирован с a name
свойство, которое является или значением непустой строки или nil
, но не может быть пустая строка:
class Document {
var name: String?
// this initializer creates a document with a nil name value
init() {}
// this initializer creates a document with a non-empty name value
init?(name: String) {
self.name = name
if name.isEmpty { return nil }
}
}
Следующий пример определяет подкласс Document
вызванный AutomaticallyNamedDocument
. AutomaticallyNamedDocument
разделите на подклассы переопределяет оба из определяемых инициализаторов, представленных Document
. Эти переопределения гарантируют что AutomaticallyNamedDocument
экземпляр имеет начальную букву name
значение "[Untitled]"
если пустая строка передается, если экземпляр инициализируется без имени, или init(name:)
инициализатор:
class AutomaticallyNamedDocument: Document {
override init() {
super.init()
self.name = "[Untitled]"
}
override init(name: String) {
super.init()
if name.isEmpty {
self.name = "[Untitled]"
} else {
self.name = name
}
}
}
AutomaticallyNamedDocument
переопределяет его failable суперкласс init?(name:)
инициализатор с nonfailable init(name:)
инициализатор. Поскольку AutomaticallyNamedDocument
справляется со случаем пустой строки по-другому, чем его суперкласс, его инициализатор не должен перестать работать, и таким образом, он обеспечивает nonfailable версию инициализатора вместо этого.
init! Инициализатор Failable
Вы обычно определяете failable инициализатор, создающий дополнительный экземпляр надлежащего типа путем размещения вопросительного знака после init
ключевое слово (init?
). Также можно определить failable инициализатор, создающий неявно развернутый дополнительный экземпляр надлежащего типа. Сделайте это путем размещения восклицательного знака после init
ключевое слово (init!
) вместо вопросительного знака.
Можно делегировать от init?
к init!
и наоборот, и можно переопределить init?
с init!
и наоборот. Можно также делегировать от init
к init!
, несмотря на то, что выполнение так инициирует утверждение если init!
инициализатор заставляет инициализацию перестать работать.
Требуемые инициализаторы
Запишите required
модификатор перед определением инициализатора класса, чтобы указать, что каждый подкласс класса должен реализовать тот инициализатор:
class SomeClass {
required init() {
// initializer implementation goes here
}
}
Необходимо также записать required
модификатор перед каждой реализацией подкласса требуемого инициализатора, чтобы указать, что требование инициализатора применяется к дальнейшим подклассам в цепочке. Вы не пишете override
модификатор при переопределении требуемого определяемого инициализатора:
class SomeSubclass: SomeClass {
required init() {
// subclass implementation of the required initializer goes here
}
}
Установка значения свойства по умолчанию с закрытием или функцией
Если значение по умолчанию сохраненного свойства требует некоторой настройки или установки, можно использовать закрытие или глобальную функцию для обеспечения специализированного значения по умолчанию для того свойства. Каждый раз, когда новый экземпляр типа, которому принадлежит свойство, инициализируется, закрытие или функцию вызывают, и ее возвращаемое значение присваивается как значение по умолчанию свойства.
Эти виды закрытий или функций обычно создают временную ценность того же типа как свойство, адаптация, которые оценивают, чтобы представлять желаемое начальное состояние, и затем возвратить ту временную ценность, которая будет использоваться в качестве значения по умолчанию свойства.
Вот скелетная схема того, как закрытие может использоваться для обеспечения значения свойства по умолчанию:
class SomeClass {
let someProperty: SomeType = {
// create a default value for someProperty inside this closure
// someValue must be of the same type as SomeType
return someValue
}()
}
Обратите внимание на то, что конец закрытия изогнутая фигурная скобка сопровождается пустой парой круглых скобок. Это говорит Свифту сразу выполнить закрытие. При исключении этих круглых скобок Вы пытаетесь присвоить само закрытие свойству, а не возвращаемому значению закрытия.
Пример ниже определяет вызванную структуру Checkerboard
, который моделирует управление по игре Средств проверки (также известный как Наброски):
В игру Средств проверки играют на десять десятью плата с чередованием черных и белых квадратов. Представлять эту игровую доску, Checkerboard
структуре вызвали единственное свойство boardColors
, который является массивом 100 Bool
значения. Значение true
в массиве представляет черный квадрат и значение false
представляет белый квадрат. Первый элемент в массиве представляет верхний левый квадрат на плате, и последний элемент в массиве представляет нижний правый квадрат на плате.
boardColors
массив инициализируется с закрытием для установки его значений цвета:
struct Checkerboard {
let boardColors: [Bool] = {
var temporaryBoard = [Bool]()
var isBlack = false
for i in 1...10 {
for j in 1...10 {
temporaryBoard.append(isBlack)
isBlack = !isBlack
}
isBlack = !isBlack
}
return temporaryBoard
}()
func squareIsBlackAtRow(row: Int, column: Int) -> Bool {
return boardColors[(row * 10) + column]
}
}
Каждый раз, когда новое Checkerboard
экземпляр создается, закрытие выполняется, и значение по умолчанию boardColors
вычисляется и возвращается. Закрытие в примере выше вычисляет и выбирает надлежащий цвет для каждого квадрата на плате в вызванном временном массиве temporaryBoard
, и возвраты этот временный массив как возвращаемое значение закрытия один раз его установка завершены. Значение возвращенного массива сохранено в boardColors
и может быть запрошен с squareIsBlackAtRow
служебная функция:
let board = Checkerboard()
println(board.squareIsBlackAtRow(0, column: 1))
// prints "true"
println(board.squareIsBlackAtRow(9, column: 9))
// prints "false"