Книга: Swift. Основы разработки приложений под iOS, iPadOS и macOS. 5-е изд. дополненное и переработанное
Назад: 31. Опциональные цепочки
Дальше: 33. Протоколы

32. Расширения

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

Перечислим возможности расширений:

• добавление вычисляемых свойств экземпляра и вычисляемых свойств типа (static);

• определение методов экземпляра и методов типа;

• определение новых инициализаторов, сабскриптов и вложенных типов;

• обеспечение соответствия существующего типа протоколу.

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

Синтаксис

extension ИмяРасширяемогоТипа {

    // описание новой функциональности для расширяемого типа

}

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

Новая функциональность, добавляемая расширением, становится доступной всем экземплярам расширяемого объектного типа вне зависимости от того, где эти экземпляры объявлены.

32.1. Вычисляемые свойства в расширениях

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

Листинг 32.1

extension Double {

    var km: Double { return self * 1000.0 }

    var m: Double { return self }

    var cm: Double { return self / 100.0 }

    var mm: Double { return self / 1000.0 }

    var ft: Double { return self / 3.28084 }

}

let oneInch = 25.4.mm

print("Один фут — это \(oneInch) метра")

// выводит "Один фут — это 0.0254 метра"

let threeFeet = 3.ft

print("Три фута — это \(threeFeet) метра")

// выводит "Три фута — это 0.914399970739201 метра"

Созданные вычисляемые свойства позволяют использовать дробные числа как конкретные единицы измерения длины. Добавленные новые свойства могут применяться для параметров и литералов типа Double.

В данном примере подразумевается, что значение 1.0 типа Double отражает величину один метр. Именно поэтому свойство m возвращает значение self.

Другие свойства требуют некоторых преобразований перед возвращением значений. Один километр — это то же самое, что 1000 метров, поэтому при запросе свойства km возвращается результат выражения self * 1000.

Чтобы после определения новых вычисляемых свойств использовать всю их мощь, требуется создавать и геттеры, и сеттеры.

ПРИМЕЧАНИЕ Расширения могут добавлять только новые вычисляемые свойства. При попытке добавить хранимые свойства или наблюдателей свойств происходит ошибка.

32.2. Инициализаторы в расширениях

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

ПРИМЕЧАНИЕ Для классов расширения могут добавлять только новые вспомогательные инициализаторы. Попытка добавить назначенный инициализатор или деинициализатор ведет к ошибке.

В качестве примера напишем инициализатор для типа Double (лис­тинг 32.2). В этом примере создается структура, описывающая линию на плоскости. Необходимо реализовать инициализатор, принимающий в качестве входного аргумента экземпляр линии и устанавливающий значение, соответствующее длине линии.

Листинг 32.2

import Foundation

// сущность "линия"

struct Line{

    var pointOne: (Double, Double)

    var pointTwo: (Double, Double)

}

// расширения для Double

extension Double {

    init(line: Line){

        self = sqrt(pow((line.pointTwo.0 - line.pointOne.0),2) +

                    pow((line.pointTwo.1 - line.pointOne.1),2))

    }

}

var myLine = Line(pointOne: (10,10), pointTwo: (14,10))

var lineLength = Double(line: myLine) // 4

Библиотека Foundation обеспечивает доступ к математическим функциям sqrt(_:) и pow(_:_:) (соответственно квадратный корень и возведение в степень), которые требуются для вычисления длины линии на плоскости.

Структура Line описывает сущность «линия», в свойствах которой указываются координаты точек ее начала и конца. Созданный в расширении инициализатор принимает на входе экземпляр класса Line и на основе значений его свойств вычисляет требуемое значение.

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

32.3. Методы в расширениях

Следующей рассматриваемой функцией расширений является создание новых методов в расширяемом типе данных. Рассмотрим пример (листинг 32.3). В этом примере путем расширения типа Int мы добавляем метод repetitions, принимающий на входе замыкание типа () -> (). Данный метод предназначен для того, чтобы выполнять переданное замыкание столько раз, сколько указывает собственное значение целого числа.

Листинг 32.3

extension Int {

    func repetitions(task: () -> ()) {

        for _ in 0..<self {

            task()

        }

    }

}

3.repetitions{

    print("Swift")

}

Консоль

Swift

Swift

Swift

Для изменения свойств перечислений и структур реализуемыми расширением методами необходимо не забывать использовать модификатор mutating. В следующем примере реализуется метод square(), который возводит в квадрат собственное значение экземпляра. Так как тип Int является структурой, то для изменения собственного значения экземпляра необходимо использовать ключевое слово mutating (листинг 32.4).

Листинг 32.4

extension Int {

    mutating func square() {

        self = self * self

    }

}

var someInt = 3

someInt.square() // 9

32.4. Сабскрипты в расширениях

Помимо свойств, методов и инициализаторов, расширения позволяют создавать новые сабскрипты.

Создаваемое в листинге 32.5 расширение типа Int реализует новый сабскрипт, который позволяет получить определенную цифру собственного значения экземпляра. В сабскрипте указывается номер позиции числа, которое необходимо вернуть.

Листинг 32.5

extension Int {

    subscript( digitIndex: Int ) -> Int {

        var base = 1

        var index = digitIndex

        while index > 0 {

            base *= 10

            index -= 1

        }

        return (self / base) % 10

    }

}

746381295[0] // 5

746381295[1] // 9

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

Назад: 31. Опциональные цепочки
Дальше: 33. Протоколы