Инициализаторы Swift — просто о сложном

Для программиста очень важно понимать что такое инициализация. И не только для iOS программирования. В объектно-ориентированных языках, как следует из их названия, работа происходит с объектами, и для этой работы объекты нужно создать, или инициализировать. Я прекрасно понимаю, что ни для кого «не открыл Америку», написав эти несколько строк. Однако, по своему опыту скажу, что до конца разобраться в процедуре инициализации в Swift достаточно сложно.

В этой статье я попытаюсь простыми словами поговорить об особенностях работы методов init(), немного затрону проваливающиеся (fail) инициализаторы init? (некоторые программисты еще их называют опциональными) и расскажу об инициализации из файла через aCoder и aDecoder

Начну с того, что для языка Swift установлены вполне определенные правила инициализации, правила отношений между инициализаторами объектов, которые были унаследованы, правила вызова инициализаторов, если их несколько у одного объекта. Эти правила описаны в документации Apple, я даже встречал их на русском языке. Но приводить их тут не буду по нескольким причинам:

  1. Честно говоря я на память и сам их не помню. Когда их читаешь — все понятно, но в документации описаны всевозможные варианты, а в реальной жизни нет такой программы где все это нужно одновременно. При необходимости я лезу в документацию и применяю их.
  2. В данной статье я хочу описать не сухие правила, а приемлемый для понимания подход, который можно запомнить и применять каждый день.

Но! Советую правила прочитать, держать их под рукой, т.к. они  однозначно определяют требования к инициализации.

Итак вот стандартный метод написания инициализатора в Swift:

//
// proSwift.ru
//

init() {
   // Тут нужно устанавливать значения в переменные экземпляра и константы.
super.init() // Вызов инициализатора суперкласса
   // Остальной код инициализации, например вызов методов.
}

The main purpose of an init method is to fill in the object’s instance variables. Any
instance variables or constants that do not have a value yet must be given one in
the init method.
Swift does not allow variables or constants to have no value (except for optionals),
and init is your last chance to make this happen.

 

Давайте рассмотрим пример:

//
// proSwift.ru
//


class FourWheel {
    
    var wheelDiametr: Int = 14
    var season = "Winter"
    
    init(wheelDiametr: Int){
        self.wheelDiametr = wheelDiametr
    }

    func changeWheel() {
        season = "Summer"
    }
}

У нас есть класс — «Четырехколесный». у него два свойства — диаметр колеса и сезонность резины, а также функция смены сезонности changeWheel() . Обратите внимание, что при написании класса были сразу присвоены значения 14 и «Winter» для соответствующих свойств класса.   Почему я обращаю внимание — напишу чуть ниже.

Также есть инициализатор  init(wheelDiametr:) который запрашивает размер резины и это запрошенное  значение устанавливает в свойство wheelDiametr.

Так вот у нашего класса два! инициализатора. Один который мы написали, а второй который Swift за нас любезно добавит. Это init(). И не смотря на то что мы его не написали, он все равно есть. Мы его описали тогда,, когда устанавливали значения в свойства. Т. е. если написать…

//
// proSwift.ru
//

let fw14 = FourWheel()
// wheelDiametr 14
// season "Winter"

… строка fw14 = FourWheel() вызовет инициализатор init(). Объект будет создан, и его свойства будут wheelDiametr = 14 и  season = «Winter».

А вот если написать …

//
// proSwift.ru
//

let fwh18 = FourWheel(wheelDiametr: 18)
// wheelDiametr 18
// season "Winter"

… то строка кода вызывает написанный нами инициализатор, который запрашивает диаметр колеса. И так как мы передаем в него число 18 то свойства объекта  fwh18 будут wheelDiametr = 18 и  season = «Winter».

Внимательный читатель спросит, а где же тот «стандартный» пример, который был озвучен в начале статьи? Мы разобрали уже два инициализатора и ни один не был похож на первый пример.  Так и есть! Просто вся соль заключается в инициализации объектов классов, которые являются наследниками других классов.

 Опять пример:

//
// proSwift.ru
//

class Car: FourWheel {
    var name: String
    var type: String
    
    
    init(name:String, type:String) {
        self.name = name
        self.type = type
 
        super.init()
        
        changeWheel()
    }
}

Класс «Машина» является наследником класса «Четырехколесный», а значит объект этого класса имеет все свойства своего суперкласса плюс еще своих два — name и type. И для инициализации объекта класса Car мы будем использовать инициализатор init(name: type:).

//
// proSwift.ru
//


let lada = Car(name: "Lada", type: "Sedan")
// {{wheelDiametr 14 season "Summer"} name "Lada" type "Sedan"}

При вызове этого инициализатора произойдет следующее: в свойства name и type установятся значения, переданные в инициализатор ( в данном случае это «Lada» и «Sedan»), а затем сработает инициализатор суперкласса — в котором в свою очередь установятся свойства wheelDiametr  и  season. Установятся они в значения 14  и «Winter», т.к. инициализатор init() задает именно эти значения. А потом произойдет вызов метода changeWheel()  и свойство  season будет иметь значение «Summer».

В данном примере мы провели инициализацию по схеме, обозначенной в начале статьи. При разработке реальных iOS-приложений на придется иметь дело с классами из фреймворков, написанных Apple, т.е. наши классы будут подклассами исходных классов. Соответственно инициализация будет происходить по вышеуказанному принципу.

Иногда можно увидеть ? после слова init. Это проваливающийся инициализатор. Он используется в том случае, если инициализатор потенциально может не сработать и возвратить нулевую величину вместо реального объекта. В качестве примера можно привести инициализацию объекта данными из файла.  Вы можете себе представить , что декодирование объекта может потерпеть неудачу, если не достаточно информации присутствует в файле или его просто не удалось прочитать.  Более подробно я планирую разобрать эти инициализатор в следующих статьях.

4 Comments on “Инициализаторы Swift — просто о сложном

  1. Строка
    let fw14 = FourWheel()
    вызовет ошибку, естественно. Проверяйте, пожалуйста, свои примеры в плейграунде.

    • Так и есть. Действительно не работает.
      Перепишу статью.
      Нашел про дефолтные инициализаторы правила
      Одно из них гласит:
      Swift предоставляет дефолтный инициализатор для любой структуры или базового класса, который имеет значение по умолчанию для всех его свойств и не имеет ни одного инициализатора
      Так что написав свой инициализатор мы не получим дефолтный.

  2. Блин надо читать коменты ) я то уже минут 20 ищу почему не могу создать объект )

    • Проблема в том, что на какой-то ранней версии языка это работало, но после обновления компилятор не пропускает. Это проблемы всех молодых языков — много вещей приходится переписывать, если обращаться к старому коду

Добавить комментарий

Ваш адрес email не будет опубликован.

*

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.