Книга: Swift. Основы разработки приложений под iOS, iPadOS и macOS. 5-е изд. дополненное и переработанное
Назад: 36. Обработка ошибок
Дальше: Часть VII. Введение в мобильную разработку

37. Нетривиальное использование операторов

Вы уже познакомились с большим количеством операторов, которые предоставляет Swift. Однако возможна ситуация, в которой для ваших собственных объектных типов данных эти операторы окажутся бесполезными. В таком случае вам потребуется самостоятельно создать свои реализации стандартных операторов или полностью новые операторы.

37.1. Операторные функции

С помощью операторных функций вы можете обеспечить взаимодействие собственных объектных типов посредством стандартных операторов Swift.

Предположим, что вы разработали структуру, описывающую вектор на плоскости (листинг 37.1).

Листинг 37.1

1  struct Vector2D {

2      var x = 0.0, y = 0.0

3  }

Свойства x и y показывают координаты конечной точки вектора. Начальная точка находится либо в точке с координатами (0, 0), либо в конечной точке предыдущего вектора.

Если перед вами возникнет задача сложить два вектора, то проще всего воспользоваться операторной функцией и создать собственную реализацию оператора сложения (+), как показано в листинге 37.2.

Листинг 37.2

func + (left: Vector2D, right: Vector2D) -> Vector2D {

    return Vector2D(x: left.x + right.x, y: left.y + right.y)

}

let vector = Vector2D(x: 3.0, y: 1.0)

let anotherVector = Vector2D(x: 2.0, y: 4.0)

let combinedVector = vector + anotherVector

Здесь операторная функция определена с именем, соответствующим оператору сложения. Так как оператор сложения является бинарным, он должен принимать два заданных значения и возвращать результат сложения.

Ситуация, когда несколько объектов имеют одно и то же имя, в Swift носит название перегрузки. С данным понятием мы уже неоднократно встречались по ходу чтения книги.

Префиксные и постфиксные операторы

Оператор сложения является бинарным инфиксным, то есть он ставится между двумя операндами. Помимо инфиксных операторов, в Swift существуют префиксные (предшествуют операнду) и постфиксные (следуют за операндом) операторы.

Для перегрузки префиксного или постфиксного оператора перед объявлением операторной функции необходимо указать модификатор prefix или postfix соответственно.

Реализуем префиксный оператор унарного минуса для структуры Vector2D (листинг 37.3).

Листинг 37.3

prefix func - (vector: Vector2D) -> Vector2D {

    return Vector2D(x: -vector.x, y: -vector.y)

}

let positive = Vector2D(x: 3.0, y: 4.0)

let negative = -positive  // negative — экземпляр Vector2D

                          // со значениями (-3.0, -4.0)

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

Составной оператор присваивания

В составных операторах присваивания оператор присваивания (+) комбинируется с другим оператором. Для перегрузки составных операторов в операторной функции первый передаваемый аргумент необходимо сделать сквозным (inout), так как именно его значение будет меняться в ходе выполнения функции.

В листинге 37.4 приведен пример реализации составного оператора присваивания-сложения для экземпляров типа Vector2D.

Листинг 37.4

func += ( left: inout Vector2D, right: Vector2D) {

    left = left + right

}

var original = Vector2D(x: 1.0, y: 2.0)

let vectorToAdd = Vector2D(x: 3.0, y: 4.0)

original += vectorToAdd

// original теперь имеет значения (4.0, 6.0)

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

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

Оператор эквивалентности

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

В следующем примере приведена реализация оператора эквивалентности и оператора неэквивалентности (листинг 37.5).

Листинг 37.5

func == (left: Vector2D, right: Vector2D) -> Bool {

    return (left.x == right.x) && (left.y == right.y)

}

func != (left: Vector2D, right: Vector2D) -> Bool {

    return !(left == right)

}

let twoThree = Vector2D(x: 2.0, y: 3.0)

let anotherTwoThree = Vector2D(x: 2.0, y: 3.0)

if twoThree == anotherTwoThree {

    print("Эти два вектора эквивалентны.")

}

// выводит "Эти два вектора эквивалентны."

В операторной функции == мы реализуем всю логику сравнения двух экземпляров типа Vector2D. Так как данная функция возвращает false в случае неэквивалентности операторов, мы можем использовать ее внутри собственной реализации оператора неэквивалентности.

37.2. Пользовательские операторы

В дополнение к стандартным операторам языка Swift вы можете определять собственные. Собственные операторы объявляются с помощью ключевого слова operator и модификаторов prefix, infix и postfix, причем вначале необходимо объявить новый оператор, а уже потом задавать его новую реализацию в виде операторной функции.

В следующем примере реализуется новый оператор +++, который складывает экземпляр типа Vector2D сам с собой (листинг 37.6).

Листинг 37.6

prefix operator +++

prefix func +++ (vector: inout Vector2D) -> Vector2D

{

    vector += vector

    return vector

}

var toBeDoubled = Vector2D(x: 1.0, y: 4.0)

let afterDoubling = +++toBeDoubled

// toBeDoubled теперь имеет значения (2.0, 8.0)

// afterDoubling также имеет значения (2.0, 8.0)

Назад: 36. Обработка ошибок
Дальше: Часть VII. Введение в мобильную разработку