Spec-Zone.ru › Swift Language

Расширения

Добавление функциональности к существующему типу.

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

Расширения в Swift могут:

  • Добавлять вычисляемые свойства экземпляров и вычисляемые свойства типа
  • Определять методы экземпляров и методы типа
  • Предоставлять новые инициализаторы
  • Определять подскрипты
  • Определять и использовать новые вложенные типы
  • Приводить существующий тип к соответствию с протоколом

В Swift можно даже расширить протокол, чтобы предоставить реализации его требований или добавить дополнительную функциональность, которой могут воспользоваться соответствующие типы. Подробнее см. <doc:Protocols#Protocol-Extensions>.

Примечание: Расширения могут добавлять новую функциональность к типу, но они не могут переопределять существующую функциональность.

Синтаксис расширения

Объявляйте расширения с помощью ключевого слова extension:

extension SomeType {
    // new functionality to add to SomeType goes here
}

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

extension SomeType: SomeProtocol, AnotherProtocol {
    // implementation of protocol requirements goes here
}

Добавление соответствия протокола таким способом описано в <doc:Protocols#Adding-Protocol-Conformance-with-an-Extension>.

Расширение можно использовать для расширения существующего обобщенного типа, как описано в <doc:Generics#Extending-a-Generic-Type>. Вы также можете расширить обобщенный тип, чтобы условно добавить функциональность, как описано в <doc:Generics#Extensions-with-a-Generic-Where-Clause>.

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

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

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

extension Double {
    var km: Double { return self * 1_000.0 }
    var m: Double { return self }
    var cm: Double { return self / 100.0 }
    var mm: Double { return self / 1_000.0 }
    var ft: Double { return self / 3.28084 }
}
let oneInch = 25.4.mm
print("One inch is \(oneInch) meters")
// Prints "One inch is 0.0254 meters"
let threeFeet = 3.ft
print("Three feet is \(threeFeet) meters")
// Prints "Three feet is 0.914399970739201 meters"

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

В этом примере значение Double 1.0 считается равным «одному метру». Вот почему вычисляемое свойство m возвращает self — выражение 1.m считается вычислением значения Double равного 1.0.

Для других единиц требуется некоторое преобразование, чтобы выразить их как значение, измеренное в метрах. Один километр эквивалентен 1000 метрам, поэтому вычисляемое свойство km умножает значение на 1_000.00 для преобразования в число, выраженное в метрах. Аналогично, в метре 3,28084 фута, поэтому вычисляемое свойство ft делит базовое значение Double на 3.28084, чтобы преобразовать его из футов в метры.

Эти свойства являются только для чтения вычисляемыми свойствами, поэтому они выражаются без ключевого слова get для краткости. Их возвращаемое значение имеет тип Double и может использоваться в математических вычислениях там, где принимается Double:

let aMarathon = 42.km + 195.m
print("A marathon is \(aMarathon) meters long")
// Prints "A marathon is 42195.0 meters long"

Примечание: Расширения могут добавлять новые вычисляемые свойства, но они не могут добавлять хранимые свойства или добавлять наблюдатели свойств к существующим свойствам.

Инициализаторы

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

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

Если вы используете расширение для добавления инициализатора к типу значения, который предоставляет значения по умолчанию для всех его хранимых свойств и не определяет никаких пользовательских инициализаторов, вы можете вызвать инициализатор по умолчанию и инициализатор по умолчанию для этого типа значения внутри инициализатора вашего расширения. Это не было бы так, если бы вы написали инициализатор в исходной реализации типа значения, как описано в <doc:Initialization#Initializer-Delegation-for-Value-Types>.

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

В примере ниже определена пользовательская структура Rect для представления геометрического прямоугольника. В примере также определены две вспомогательные структуры, называемые Size и Point, обе из которых предоставляют значения по умолчанию 0.0 для всех своих свойств:

struct Size {
    var width = 0.0, height = 0.0
}
struct Point {
    var x = 0.0, y = 0.0
}
struct Rect {
    var origin = Point()
    var size = Size()
}

Поскольку структура Rect предоставляет значения по умолчанию для всех своих свойств, она автоматически получает инициализатор по умолчанию и инициализатор по умолчанию, как описано в <doc:Initialization#Default-Initializers>. С помощью этих инициализаторов можно создавать новые экземпляры Rect:

let defaultRect = Rect()
let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0),
    size: Size(width: 5.0, height: 5.0))

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

extension Rect {
    init(center: Point, size: Size) {
        let originX = center.x - (size.width / 2)
        let originY = center.y - (size.height / 2)
        self.init(origin: Point(x: originX, y: originY), size: size)
    }
}

Этот новый инициализатор начинает с вычисления соответствующей точки начала координат на основе предоставленной точки center и значения size. Затем инициализатор вызывает автоматический инициализатор по умолчанию структуры init(origin:size:), который сохраняет новые значения начала координат и размера в соответствующих свойствах:

let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
    size: Size(width: 3.0, height: 3.0))
// centerRect's origin is (2.5, 2.5) and its size is (3.0, 3.0)

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

Методы

Расширения могут добавлять новые методы экземпляров и методы типа к существующим типам. В следующем примере добавляется новый метод экземпляра, называемый repetitions, к типу Int:

extension Int {
    func repetitions(task: () -> Void) {
        for _ in 0..<self {
            task()
        }
    }
}

Метод repetitions(task:) принимает один аргумент типа () -> Void, который указывает на функцию без параметров и без возвращаемого значения.

После определения этого расширения вы можете вызвать метод repetitions(task:) для любого целого числа, чтобы выполнить задачу много раз:

3.repetitions {
    print("Hello!")
}
// Hello!
// Hello!
// Hello!

Мутирующие методы экземпляров

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

В примере ниже добавляется новый изменяющий метод, называемый square, к типу Int Swift, который возводит исходное значение в квадрат:

extension Int {
    mutating func square() {
        self = self * self
    }
}
var someInt = 3
someInt.square()
// someInt is now 9

Подскрипты

Расширения могут добавлять новые подскрипты к существующему типу. В этом примере добавляется целочисленный подскрипт к встроенному в Swift типу Int. Этот подскрипт [n] возвращает десятичную цифру n места вправо от числа:

  • 123456789[0] возвращает 9
  • 123456789[1] возвращает 8

…и так далее:

extension Int {
    subscript(digitIndex: Int) -> Int {
        var decimalBase = 1
        for _ in 0..<digitIndex {
            decimalBase *= 10
        }
        return (self / decimalBase) % 10
    }
}
746381295[0]
// returns 5
746381295[1]
// returns 9
746381295[2]
// returns 2
746381295[8]
// returns 7

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

746381295[9]
// returns 0, as if you had requested:
0746381295[9]

Вложенные типы

Расширения могут добавлять новые вложенные типы к существующим классам, структурам и перечислениям:

extension Int {
    enum Kind {
        case negative, zero, positive
    }
    var kind: Kind {
        switch self {
        case 0:
            return .zero
        case let x where x > 0:
            return .positive
        default:
            return .negative
        }
    }
}

В этом примере добавляется новое вложенное перечисление к Int. Это перечисление, называемое Kind, выражает тип числа, которое представляет конкретное целое число. То есть, выражает, является ли число отрицательным, нулевым или положительным.

В этом примере также добавляется новое вычисляемое свойство экземпляра к Int, называемое kind, которое возвращает соответствующий случай перечисления Kind для этого целого числа.

Теперь вложенное перечисление может быть использовано со значением любого целого числа Int:

func printIntegerKinds(_ numbers: [Int]) {
    for number in numbers {
        switch number.kind {
        case .negative:
            print("- ", terminator: "")
        case .zero:
            print("0 ", terminator: "")
        case .positive:
            print("+ ", terminator: "")
        }
    }
    print("")
}
printIntegerKinds([3, 19, -27, 0, -6, 0, 7])
// Prints "+ + - 0 - 0 + "

Эта функция, printIntegerKinds(_:), принимает входной массив значений Int и итерирует по этим значениям по очереди. Для каждого целого числа в массиве функция учитывает вычисляемое свойство kind для этого целого числа и печатает соответствующее описание.

Примечание: number.kind уже известно как типа Int.Kind. Из-за этого все значения случая Int.Kind могут быть записаны в сокращенной форме внутри оператора switch, например .negative вместо Int.Kind.negative.

This source file is part of the Swift.org open source project
Copyright © 2014 - 2025 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API