Перейти к содержимому
Теория · Классы · урок 9 из 10

Структура или класс

Теперь у вас две картинки. Структура — бланк: при присваивании делают ксерокопию, и копию заполняют отдельно. Класс — общий документ: к нему ведут бирки, и правку через одну бирку видно через все. Что выбрать, структуру или класс, зависит от того, что должна делать программа.

Возьмём игру. У игрока есть очки, и с ним работают сразу несколько частей программы: команда, таблица рекордов, экран профиля. Очки должны совпадать везде, значит, игрок — один общий экземпляр:

SWIFT
class Player {
    var score = 0
}

let hero = Player()
let team = [hero]
hero.score += 5
print(team[0].score) // 5
print(team[0] === hero) // true

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

  1. hero — ссылка на экземпляр Player.
  2. team = [hero] кладёт в массив ещё одну ссылку на тот же экземпляр. Массив копирует ссылку, а не игрока.
  3. hero.score += 5 меняет общего игрока, поэтому team[0].score тоже равно 5.
  4. team[0] === hero отвечает true: это один экземпляр.

А позиция фигуры на поле — это только число. Если сохранить стартовую позицию и потом двигать фигуру, стартовая должна остаться прежней. Здесь нужна копия, то есть структура:

SWIFT
struct Position {
    var x = 0
}

let start = Position()
var current = start
current.x += 3
print(start.x, current.x) // 0 3

current — копия start, поэтому сдвиг current не трогает start, и программа печатает 0 3.

Как выбрать

Структура — по умолчанию

Apple советует начинать со структуры. Берите её, когда значение описывает данные: позицию, сумму денег, настройки. Каждый, кто получил структуру, работает со своей копией, и никто не изменит её у вас за спиной.

Класс — для общего экземпляра

Берите класс, если верно хотя бы одно:

  • несколько частей программы меняют одну и ту же вещь и должны видеть правки друг друга: игрока, банковский счёт, открытый документ;
  • важно, тот же это экземпляр или другой с такими же свойствами, то есть нужна проверка ===;
  • нужно наследование.

Здесь картинка с ксерокопией неточна. Настоящая ксерокопия стоит бумаги, а копия небольшой структуры почти ничего не стоит. Массив внутри неё Swift копирует только перед первым изменением, как вы видели в теме о массивах. Поэтому брать класс, чтобы сэкономить на копиях, не нужно.

Ответ на собеседовании

«Чем структура отличается от класса?» — один из самых частых вопросов на собеседовании по Swift. Ответ можно строить так:

  1. Структура — значимый тип: присваивание и передача в функцию копируют значение. Класс — ссылочный: копируется ссылка, и все имена ведут к одному экземпляру.
  2. let у структуры замораживает всё значение, а у класса — только ссылку.
  3. Методы структуры, меняющие свойства, помечают mutating; методам класса это не нужно.
  4. Структура сама получает почленный инициализатор, а классу со свойством без значения по умолчанию пишут свой init.
  5. Только у классов есть наследование и проверка ===.

Закончите тем, как выбирать: по умолчанию структура, а класс — когда нужен общий экземпляр, проверка === или наследование.

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

Класс взяли для данных, которые должны копироваться. Позицию сохранили, а потом сдвинули фигуру:

SWIFT
class Spot { // ✗
    var x = 0
}

let saved = Spot()
let piece = saved
piece.x += 3
print(saved.x) // 3

Программа печатает 3, а не 0: let piece = saved скопировала ссылку, и сдвиг фигуры изменил сохранённую позицию. Позиция — это данные, поэтому Spot должен быть структурой. Тогда piece объявляют через var, и программа печатает 0.

Главное

  • Структура копируется при присваивании и передаче, а экземпляр класса остаётся общим для всех ссылок.
  • По умолчанию выбирают структуру.
  • Класс нужен, когда несколько частей программы меняют одну вещь, важна проверка === или нужно наследование.
  • Наследование и === есть только у классов, а mutating и почленный инициализатор — у структур.