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

Делегирование инициализатора для типов значений

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

Инициализаторы могут вызывать другие инициализаторы для инициализации части экземпляра. Этот процесс называется как делегирование инициализатора. Он позволяет избегать дублирования кода в разных инициализаторах.

Правила того, как работает делегирование инициализатора и для каких форм делегирования это возможно, для типов значений и ссылочных типов разные. Типы значений (структуры и перечисления) не поддерживают наследование, так что их процесс делегирования инициализатора сравнительно прост, потому что они только могут делегировать другому инициализатору то, что предоставляют сами. Классы, однако, могут наследовать от других классов, как это описано в Наследование. Это значит, что у классов есть дополнительная ответственность за проверку наличия корректных значений у каждого унаследованного свойства хранения класса. Обязанности описаны в главе Наследование и инициализация класса.

Для типов значений вы используете self.init для ссылки на остальные инициализаторы одного и того же типа значения, когда вы пишите свои инициализаторы. Вы можете вызывать self.init из инициализатора.

Обратите внимание, что если вы определите пользовательский инициализатор для типов значений, то вы больше не будете иметь доступа к дефолтному инициализатору (или почленному инициализатору, если это структура) для этого типа. Такое ограничение предотвращает ситуацию, в которой настройка важного дополнения в более сложном инициализаторе может быть пропущена при случайном использовании автоматического инициализатора.

Заметка

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

Следующий пример определяет пользовательскую структуру 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() функционально тот же самый, что и дефолтный инициализатор, который бы получила структура, если бы не имела пользовательских инициализаторов. Инициализатор имеет пустое тело, отображенное парой пустых фигурных скобок {}, и не проводит никакой инициализации. Вызывая такой инициализатор, мы возвращаем экземпляр Rect, который имеет инициализированные свойства origin, size значениями Point(x: 0.0, y: 0.0) и Size(width: 0.0, height: 0.0), которые известны из определения свойств:

let basicRect = Rect()
//исходная точка Rect (0.0, 0.0) и его размер (0.0, 0.0)

Второй инициализатор Rect - init(origin:size:) функционально то же самое что и почленный инициализатор, который могла бы иметь структура, если бы не имела пользовательских инициализаторов. Этот инициализатор просто присваивает значения аргументов origin, size соответствующим свойствам:

let originRect = Rect(origin: Point(x: 2.0, y: 2.0), size: Size(width: 5.0, height: 5.0))
//исходная точка Rect (2.0, 2.0) и его размер (5.0, 5.0)

Третий инициализатор Rect - init(center:size:) немного более сложный. Он начинается с вычисления соответствующей исходной точки, основываясь на точке center и значении size. Только потом он вызывает ( или делегирует) init(origin:size:) инициализатор, который хранит новую исходную точку и значения размеров соответствующих свойств:

let centerRect = Rect(center: Point(x: 4.0, y: 4.0), size: Size(width: 3.0, height: 3.0))
//исходная точка centerRect’а равна (2.5, 2.5) и его размер (3.0, 3.0)

Инициализатор init(center:size:) мог бы присвоить новые значения origin, size соответствующим свойствам самостоятельно. Однако более удобно (т.к. более понятный из-за краткости) для инициализатора init(center:size:) воспользоваться преимуществом того, что существует другой инициализатор с абсолютно такой же функциональностью.

Заметка

Для альтернативного способа записи этого примера без инициализаторов init(), init(origin:size:) смотрите главу Расширения.

 

Swift: 
3.0