Перейти к содержимому
Теория · Перечисления · урок 6 из 8

Методы перечислений

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

В первом уроке цену считала отдельная функция price(of:), и ей передавали напиток. Код о напитках можно держать и внутри самого перечисления. Тогда вариант сам отвечает на вопрос о себе:

SWIFT
enum Drink {
    case tea, coffee

    func price(cups: Int) -> Int {
        switch self {
        case .tea:
            return 2 * cups
        case .coffee:
            return 3 * cups
        }
    }
}

let drink = Drink.coffee
print(drink.price(cups: 2)) // 6

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

  1. Функция price(cups:) объявлена внутри фигурных скобок перечисления. Такая функция — метод перечисления, как hasSuffix у строки.
  2. drink.price(cups: 2) вызывает метод через точку у значения drink.
  3. Внутри метода self — то значение, у которого метод вызвали. Здесь это drink, то есть кофе.
  4. switch self выбирает ветку .coffee, и метод возвращает 3 * 2, то есть 6.

Метод и self

Метод объявляют так же, как обычную функцию, только внутри перечисления, обычно после списка вариантов. Что важно знать о методе:

  • его вызывают у значения через точку: drink.price(cups: 2) или Drink.tea.price(cups: 3);
  • self внутри метода — само это значение, «эта строка меню»;
  • switch self — обычное тело такого метода: у каждого варианта своя ветка, и default не нужен.

Вычисляемое свойство

У строки есть count: его читают через точку и без скобок. То, что читают у значения через точку без скобок, называют свойством. Перечислению тоже можно дать свойство, только вычислять его будет ваш код.

Пусть теперь на чеке кофе записано число чашек. Цена заказа зависит от этого числа:

SWIFT
enum Order {
    case tea
    case coffee(cups: Int)

    var price: Int {
        switch self {
        case .tea:
            return 2
        case .coffee(let cups):
            return cups * 3
        }
    }
}

print(Order.coffee(cups: 2).price) // 6

Вычисляемое свойство — свойство, значение которого нигде не хранится, а вычисляется заново при каждом чтении. Его объявление состоит из трёх частей:

  • var — вычисляемое свойство объявляют только через var: с let код не скомпилируется;
  • price: Int — имя свойства и его тип;
  • фигурные скобки — код, который возвращает значение через return.

Читают свойство без скобок: .price, а не .price(). В ветке .coffee(let cups) шаблон достаёт число чашек, и для двух чашек свойство даёт 6.

Короткую ветку можно записать и в одну строку: case .tea: return 2. Это та же ветка, только без переноса строки.

Когда писать метод, а когда свойство? Свойство подходит, когда ответу ничего не нужно, кроме самого значения: цена заказа, размер чашки. Метод нужен, когда ответу нужны данные извне, как число чашек в price(cups:).

Здесь картинка с меню неточна. Цену в меню печатают один раз, а вычисляемое свойство ничего не хранит: price выполняет switch при каждом чтении.

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

Метод вызвали у самого типа, а не у значения:

SWIFT
print(Drink.price(cups: 2)) // ✗
TEXT
error: instance member 'price' cannot be used on type 'Drink'; did you mean to use a value of this type instead?

Программа не компилируется. Drink — всё меню, а не один напиток, и непонятно, чью цену считать: чая или кофе. Компилятор подсказывает, что нужно значение этого типа. Вызовите метод у варианта, Drink.tea.price(cups: 2), или у константы: drink.price(cups: 2).

Главное

  • Метод перечисления объявляют внутри его фигурных скобок и вызывают у значения через точку: drink.price(cups: 2).
  • self внутри метода — значение, у которого его вызвали, а switch self выбирает ветку по варианту.
  • Вычисляемое свойство ничего не хранит: его код выполняется при каждом чтении, а читают его без скобок.