Перейти к содержимому
Теория · Структуры · урок 1 из 9

Структуры

Чему вы научитесь

Чему вы научитесь:

  • описывать свой тип структурой: свойства, значения по умолчанию, создание экземпляра;
  • писать методы и объяснять, зачем методу нужно слово mutating;
  • добавлять вычисляемые свойства, наблюдатели willSet и didSet, свой init и static;
  • объяснять, почему изменение копии структуры не трогает оригинал.
Бланк Point с полем x: 1 и его ксерокопия, где поле исправлено на x: 10, а оригинал не изменился

Представьте бумажный бланк. В шапке напечатано название Point, ниже две графы с подписями: x и y. Пустой бланк — это образец: он говорит, какие графы есть и что в них писать. Заполненный бланк хранит данные: в графе x стоит 1, в графе y — 2.

Бланк можно отксерокопировать. На копии графу x зачёркивают и пишут 10, а на оригинале по-прежнему x: 1. Бланки, заполненные по одному образцу, живут каждый сам по себе.

Зачем такой бланк в программе? Точка на плоскости — это два числа, и их можно держать в двух переменных, x и y. Но когда точек станет 10, переменных будет 20, и придётся следить, какой y относится к какому x. Удобнее собрать оба числа под одним именем. Для этого в Swift есть структуры:

SWIFT
struct Point {
    var x: Int
    var y: Int
}

let start = Point(x: 1, y: 2)
print(start.x) // 1
print(start.y) // 2
print(start) // Point(x: 1, y: 2)

Давайте разберём программу по шагам:

  1. Строки от struct до закрывающей фигурной скобки описывают образец бланка Point. Сами по себе они ничего не создают.
  2. Point(x: 1, y: 2) заполняет новый бланк: в графу x пишет 1, в графу y — 2. Готовый бланк попадает в константу start.
  3. start.x и start.y читают графы через точку, и программа печатает 1 и 2.
  4. print(start) печатает бланк целиком: имя типа и все графы со значениями.

Объявление структуры

Структура — это свой тип, который собирает несколько значений под одним именем. Объявление struct Point состоит из трёх частей:

  • struct — ключевое слово, с которого начинается объявление;
  • Point — имя нового типа; имена типов пишут с большой буквы, как Int и String;
  • фигурные скобки, а в них — графы бланка, каждая со своим именем и типом.

Графы структуры — это её свойства: их, как и у перечислений, читают через точку без скобок. Но свойство структуры хранит значение, и объявляют его как обычную переменную или константу: var x: Int.

Новым типом пользуются так же, как Int или String. Можно объявить константу типа Point, передать точку в функцию или сложить несколько точек в массив [Point].

Создание экземпляра

Конкретное значение типа Point, заполненный бланк, называют экземпляром структуры. Чтобы создать экземпляр, пишут имя типа, а в скобках — значения всех свойств с метками, как аргументы при вызове функции. Swift даёт структуре такой способ создания сам: писать для него ничего не нужно.

Правила у него такие:

  • метки совпадают с именами свойств: x: и y:;
  • порядок тот же, что в объявлении: Point(y: 2, x: 1) не скомпилируется с ошибкой argument 'x' must precede argument 'y';
  • значение нужно каждому свойству: Point(x: 1) даёт ошибку missing argument for parameter 'y' in call.

Здесь картинка с бланком неточна. Бумажный бланк можно сдать с пустой графой, а у экземпляра структуры заполнены все свойства. Swift не создаст Point, пока не узнает и x, и y.

Значения по умолчанию

Иногда в бланке уже напечатано значение, и его исправляют, только когда нужно. В структуре такое значение пишут прямо в объявлении свойства:

SWIFT
struct Alarm {
    var hour = 7
    var minute = 30
}

let usual = Alarm()
let early = Alarm(hour: 6)
print(usual) // Alarm(hour: 7, minute: 30)
print(early) // Alarm(hour: 6, minute: 30)

Свойство со значением по умолчанию при создании можно пропустить:

  • Alarm() берёт оба значения по умолчанию: 7 часов и 30 минут;
  • Alarm(hour: 6) задаёт час, а минуты оставляет по умолчанию.

Тип свойств здесь не указан: Swift выводит его из значения, как у обычной переменной.

Чтение и изменение через точку

Свойство читают через точку: start.x. Через точку же его меняют, если экземпляр хранится в переменной var:

SWIFT
var finish = Point(x: 1, y: 2)
finish.x = 10
print(finish) // Point(x: 10, y: 2)

finish.x = 10 исправляет одну графу, а y остаётся прежним.

Менять свойство можно, только если это разрешают два объявления сразу:

  • экземпляр объявлен через var: у экземпляра в let нельзя переписать ни одно свойство;
  • само свойство объявлено через var: свойство let не изменится даже у экземпляра в var.

Частая ошибка

Свойство поменяли у экземпляра, объявленного через let:

SWIFT
start.x = 10 // ✗
TEXT
error: cannot assign to property: 'start' is a 'let' constant

Программа не компилируется. Константа start — запечатанный бланк: менять нельзя ни его целиком, ни отдельные графы, хотя x в Point объявлен через var. Если бланк нужно исправлять, объявите экземпляр через var, как finish.

Главное

  • Структура собирает несколько значений под одним именем: после struct идут имя типа и свойства в фигурных скобках.
  • Экземпляр создают с метками всех свойств по порядку: Point(x: 1, y: 2); свойство со значением по умолчанию можно пропустить.
  • Свойство читают и меняют через точку: finish.x = 10.
  • Экземпляр в let не меняется совсем: ни одно его свойство переписать нельзя.