Расширения добавляют новые функциональные возможности к существующему классу, структуре, перечислению или типу протокола. Это включает в себя возможность расширять типы, для которых у вас нет доступа к исходному исходному коду (известный как ретроактивное моделирование ). Расширения аналогичны категориям в Objective-C. (В отличие от категорий Objective C, расширения Swift не имеют имен.)
Расширения в Swift могут:
- Добавить свойства вычисляемого экземпляра и свойства вычисляемого типа
- Определите методы экземпляра и методы типа
- Предоставить новые инициализаторы
- Определить подписки
- Определите и используйте новые вложенные типы
- Привести существующий тип в соответствие с протоколом
В Swift вы можете даже расширить протокол, чтобы обеспечить реализацию его требований или добавить дополнительные функциональные возможности, которыми могут воспользоваться соответствующие типы.
ЗАМЕТКА
Расширения могут добавлять новые функциональные возможности к типу, но они не могут переопределять существующие функциональные возможности.
Синтаксис расширения
Объявите расширения с extension
ключевым словом:
extension SomeType {
// new functionality to add to SomeType goes here
}
Расширение может расширять существующий тип, чтобы заставить его принимать один или несколько протоколов. Чтобы добавить соответствие протокола, вы пишете имена протоколов так же, как вы пишете их для класса или структуры:
extension SomeType: SomeProtocol, AnotherProtocol {
// implementation of protocol requirements goes here
}
Расширение может использоваться для расширения существующего универсального типа. Вы также можете расширить универсальный тип для условного добавления функциональности.
ЗАМЕТКА
Если вы определяете расширение для добавления новой функциональности к существующему типу, новая функциональность будет доступна во всех существующих экземплярах этого типа, даже если они были созданы до того, как было определено расширение.
Вычисленные свойства
Расширения могут добавлять свойства вычисляемых экземпляров и свойства вычисляемых типов к существующим типам. В этом примере добавляется пять свойств вычисляемого экземпляра к встроенному Double
типу Swift , чтобы обеспечить базовую поддержку работы с единицами расстояния:
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"
ЗАМЕТКА
Расширения могут добавлять новые вычисляемые свойства, но они не могут добавлять сохраненные свойства или добавлять наблюдатели свойств к существующим свойствам.
Инициализаторы
Расширения могут добавлять новые инициализаторы к существующим типам. Это позволяет вам расширять другие типы, чтобы принимать ваши собственные пользовательские типы в качестве параметров инициализатора, или предоставлять дополнительные параметры инициализации, которые не были включены как часть первоначальной реализации типа.
Расширения могут добавлять новые удобные инициализаторы в класс, но они не могут добавлять новые назначенные инициализаторы или деинициализаторы в класс. Назначенные инициализаторы и деинициализаторы всегда должны предоставляться исходной реализацией класса.
Если вы используете расширение для добавления инициализатора к типу значения, который предоставляет значения по умолчанию для всех его сохраненных свойств и не определяет никаких пользовательских инициализаторов, вы можете вызвать инициализатор по умолчанию и элементный инициализатор для этого типа значения из инициализатора вашего расширения. Это было бы не так, если бы вы написали инициализатор как часть первоначальной реализации типа значения,.
Если вы используете расширение для добавления инициализатора к структуре, которая была объявлена в другом модуле, новый инициализатор не сможет получить доступ, 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
структура предоставляет значения по умолчанию для всех своих свойств, она автоматически получает инициализатор по умолчанию и инициализатор по элементам. Эти инициализаторы могут быть использованы для создания новых 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
Нижние индексы
Расширения могут добавлять новые подписки к существующему типу. В этом примере добавляется целочисленный индекс к встроенному Int
типу Swift . Этот нижний индекс [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
named 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
.
0 комментариев