Видеокурсы по изучению языка программирования Swift. Подробнее

Установка начальных значений для свойств хранения

Если вы нашли опечатку в тексте, выделите ее и нажмите CTRL + ENTER.

Классы и структуры должны устанавливать начальные значения у всех свойств хранения во время создания класса или структуры. Свойства хранения не могут быть оставлены в неопределённом состоянии.

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

Заметка

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

Инициализаторы

Инициализаторы вызываются для создания нового экземпляра конкретного типа. В самой простой своей форме инициализатор работает как метод экземпляра без параметров, написанный с помощью ключевого слова init:

init() {
    // инициализация проводится тут
}

Пример ниже определяет новую структуру Fahrenheit для хранения температур, представленных в Фаренгейтах. Структура Fahrenheit имеет всего одно свойство, temperature типа Double:

struct Fahrenheit {
    var temperature: Double
    init() {
        temperature = 32.0
    }
}
var f = Fahrenheit()
print("Значение температуры по умолчанию \(f.temperature)° по Фаренгейту")
// Выведет "Значение температуры по умолчанию 32.0° по Фаренгейту"

Структура определяет один инициализатор, 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

Первый инициализатор имеет один параметр с внешнем именем fromFahrinheit и с локальным именем fahrenheit. Второй инициализатор имеет один параметр с внешнем именем fromKelvin и локальным именем kelvin. Оба инициализатора конвертируют их единственный аргумент в значение по Цельсию и сохраняют это значение в свойство temperatureInCelsius.

Локальные и внешние имена параметров

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

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

Следующий пример определяет структуру Color с тремя постоянными свойствами red, green, blue. Эти свойства имеют значения от 0.0 до 1.0, для индикации количества соответствующего цвета.

Color имеет инициализатор с тремя параметрами red, green, blue типа 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)
// этот код вызовет ошибку компиляции, так как здесь нет внешних имен

Параметры инициализатора без внешних имен

Если вы не хотите использовать внешние имена для параметров инициализации, напишите подчеркивание (_) вместо явного указания внешнего имени для этого параметра, чтобы переопределить поведение по умолчанию.

Вот расширенный вариант для Celsius, который мы рассматривали ранее, с дополнительным инициализатором, для создания нового экземпляра Celsius с типом значения температуры 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() {
        print(text)
    }
}
let cheeseQuestion = SurveyQuestion(text: "Нравится ли вам сыр?")
cheeseQuestion.ask()
// Выведет "Нравится ли вам сыр?"
cheeseQuestion.response = "Да, я люблю сыр"

Ответ на вопрос не может быть известен до того, пока он не задан, так что свойство response должно быть типа String? (опциональный String). Ему автоматически присваивается значение nil при инициализации SurveyQuestion, значащее, что “значения пока нет”.

Присваивание значений постоянным свойствам во время инициализации

Вы можете присвоить значения постоянных (когда свойство константа) свойств в любой точке вашего процесса инициализации.

Заметка

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

Вы можете пересмотреть пример SurveyQuestion и использовать вместо переменного свойства text постоянное свойство text, для индикации того, что это свойство не меняется после создания SurveyQuestion. Даже если свойство является постоянным, оно все еще может быть установлено в инициализаторе класса:

class SurveyQuestion {
  let text: String
  var response: String?
  init(text: String) {
    self.text = text
  }
  func ask() {
    print(text)
  }
}
let beetsQuestion = SurveyQuestion(text: "Что насчет свеклы?")
beetsQuestion.ask()
// Выведет "Что насчет свеклы?"
beetsQuestion.response = "Я люблю свеклу, но не в сыром виде!"

 

Swift: 
4.0