Объявления
Введение типов, операторов, переменных и других имён и конструкций.
Объявление вводит новое имя или конструкцию в вашу программу. Например, вы используете объявления для введения функций и методов, для введения переменных и констант, а также для определения перечислений, структур, классов и типов протоколов. Вы также можете использовать объявление для расширения поведения существующего именованного типа и для импорта символов в вашу программу, объявленных где-то ещё.
В Swift большинство объявлений также являются определениями в том смысле, что они реализуются или инициализируются одновременно с объявлением. Тем не менее, поскольку протоколы не реализуют свои члены, большинство членов протокола являются только объявлениями. Для удобства и потому, что различие не так важно в Swift, термин объявление охватывает как объявления, так и определения.
Грамматика объявления:
объявление → объявление-импорта
объявление → объявление-константы
объявление → объявление-переменной
объявление → объявление-типа-псевдонима
объявление → объявление-функции
объявление → объявление-перечисления
объявление → объявление-структуры
объявление → объявление-класса
объявление → объявление-актора
объявление → объявление-протокола
объявление → объявление-инициализатора
объявление → объявление-деинициализатора
объявление → объявление-расширения
объявление → объявление-индексатора
объявление → объявление-макроса
объявление → объявление-оператора
объявление → объявление-группы-прецедентности
Код верхнего уровня
Код верхнего уровня в файле исходного кода Swift состоит из нуля или более операторов, объявлений и выражений. По умолчанию переменные, константы и другие именованные объявления, объявленные на верхнем уровне файла исходного кода, доступны коду в каждом файле исходного кода, который является частью того же модуля. Вы можете переопределить это поведение по умолчанию, пометив объявление модификатором уровня доступа, как описано в <doc:Объявления#Уровни-контроля-доступа>.
Существует два типа кода верхнего уровня: объявления верхнего уровня и исполняемый код верхнего уровня. Объявления верхнего уровня состоят только из объявлений и разрешены во всех файлах исходного кода Swift. Исполняемый код верхнего уровня содержит операторы и выражения, а не только объявления, и разрешён только в качестве точки входа в программу.
Код Swift, который вы компилируете, чтобы создать исполняемый файл, может содержать не более одного из следующих подходов для обозначения точки входа верхнего уровня, независимо от того, как код организован в файлы и модули: атрибут main, атрибут NSApplicationMain, атрибут UIApplicationMain, файл main.swift или файл, содержащий исполняемый код верхнего уровня.
Грамматика объявления верхнего уровня:
объявление-верхнего-уровня → операторы_?_
Блоки кода
Блок кода используется различными объявлениями и управляющими структурами для группировки операторов вместе. Он имеет следующий вид:
{
<#statements#>
} Операторы внутри блока кода включают объявления, выражения и другие типы операторов и выполняются в порядке их появления в исходном коде.
Грамматика блока кода:
блок-кода →
{операторы_?_}
Объявление импорта
Объявление импорта позволяет получить доступ к символам, объявленным вне текущего файла. Основной вид импортирует весь модуль; он состоит из ключевого слова import, за которым следует имя модуля:
import <#module#>
Предоставление дополнительных данных ограничивает, какие символы импортируются — вы можете указать конкретный подмодуль или конкретное объявление внутри модуля или подмодуля. Когда используется эта подробная форма, в текущем объёме доступен только импортированный символ (а не модуль, в котором он объявлен).
import <#import kind#> <#module#>.<#symbol name#> import <#module#>.<#submodule#>
Грамматика объявления импорта:
объявление-импорта → атрибуты_?_
importвид-импорта_?_ путь-импортавид-импорта →
typealias|struct|class|enum|protocol|let|var|func
путь-импорта → идентификатор | идентификатор.путь-импорта
Объявление константы
Объявление константы вводит именованное постоянное значение в вашу программу. Объявления констант объявляются с помощью ключевого слова let и имеют следующий вид:
let <#constant name#>: <#type#> = <#expression#>
Объявление константы определяет неизменяемую связь между именем константы и значением выражения инициализатора; после того, как значение константы установлено, его нельзя изменить. Тем не менее, если константа инициализируется объектом класса, сам объект может измениться, но связь между именем константы и объектом, на который она ссылается, не может.
Когда константа объявляется на глобальном уровне, она должна быть инициализирована значением. Когда объявление константы происходит в контексте функции или метода, она может быть инициализирована позже, если гарантируется, что её значение установлено до первого чтения её значения. Если компилятор может доказать, что значение константы никогда не считывается, константа вообще не обязана иметь установленное значение. Этот анализ называется определённой инициализацией — компилятор доказывает, что значение установлено определённо перед считыванием.
Примечание: Определённая инициализация не может создавать доказательства, которые требуют предметных знаний, и её способность отслеживать состояние через условные операторы имеет ограничение. Если вы можете определить, что константа всегда имеет установленное значение, но компилятор не может доказать это, попробуйте упростить пути, которые устанавливают значение, или используйте объявление переменной вместо этого.
Когда объявление константы происходит в контексте объявления класса или структуры, оно считается постоянным свойством. Объявления констант не являются вычисляемыми свойствами и поэтому не имеют методов получения или задания.
Если имя константы в объявлении константы является образцом кортежа, имя каждого элемента кортежа связывается с соответствующим значением в выражении инициализатора.
let (firstNumber, secondNumber) = (10, 42)
В этом примере firstNumber является именованной константой для значения 10, а secondNumber является именованной константой для значения 42. Обе константы теперь могут использоваться независимо:
print("The first number is \(firstNumber).")
// Prints "The first number is 10."
print("The second number is \(secondNumber).")
// Prints "The second number is 42." Аннотация типа (: тип) необязательна в объявлении константы, когда тип имени константы может быть выведен, как описано в <doc:Типы#Выведение-типов>.
Для объявления свойства типа константы пометьте объявление модификатором static. Постоянное свойство типа класса всегда неявно final; вы не можете пометить его модификаторами class или final, чтобы разрешить или запретить переопределение подклассами. Свойства типов обсуждаются в <doc:Свойства#Свойства-типов>.
Дополнительную информацию о константах и рекомендации по их использованию см. в <doc:Основы#Константы-и-переменные> и <doc:Свойства#Сохранённые-свойства>.
Грамматика объявления константы:
объявление-константы → атрибуты_?_ модификаторы-объявления_?_
letсписок-инициализаций-образовсписок-инициализаций-образов → инициализация-образца | инициализация-образца
,список-инициализаций-образов
инициализация-образца → образец инициализатор_?_
инициализатор →=выражение
Объявление переменной
Объявление переменной вводит именованное изменяемое значение в вашу программу и объявляется с помощью ключевого слова var.
Объявления переменных имеют несколько форм, которые объявляют различные типы именованных, изменяемых значений, включая сохранённые и вычисляемые переменные и свойства, наблюдатели сохранённых переменных и свойств, и статические свойства переменных. Подходящая форма зависит от области, в которой переменная объявляется, и от типа переменной, которую вы хотите объявить.
Примечание: Вы также можете объявить свойства в контексте объявления протокола, как описано в <doc:Объявления#Объявление-свойства-протокола>.
Вы можете переопределить свойство в подклассе, пометив объявление свойства подкласса модификатором override объявления, как описано в <doc:Наследование#Переопределение>.
Сохранённые переменные и сохранённые свойства переменных
Следующая форма объявляет сохранённую переменную или сохранённое свойство переменной:
var <#variable name#>: <#type#> = <#expression#>
Вы определяете эту форму объявления переменной на глобальном уровне, в локальной области функции или в контексте объявления класса или структуры. Когда объявление переменной в этой форме объявляется на глобальном уровне или в локальной области функции, оно называется сохранённой переменной. Когда оно объявляется в контексте объявления класса или структуры, оно называется сохранённым свойством переменной.
Выражение инициализатора выражение не может присутствовать в объявлении протокола, но во всех других контекстах выражение инициализатора выражение является необязательным. Тем не менее, если выражение инициализатора выражение отсутствует, объявление переменной должно включать явную аннотацию типа (: тип).
Как и в объявлениях констант, если в объявлении переменной опускается выражение инициализатора, переменная должна иметь значение, установленное до первого чтения её значения. Также, как и в объявлениях констант, если имя переменной является образцом кортежа, имя каждого элемента кортежа связывается с соответствующим значением в выражении инициализатора.
Как следует из их названий, значение сохранённой переменной или сохранённого свойства переменной хранится в памяти.
Вычисляемые переменные и вычисляемые свойства
Следующая форма объявляет вычисляемую переменную или вычисляемое свойство:
var <#variable name#>: <#type#> {
get {
<#statements#>
}
set(<#setter name#>) {
<#statements#>
}
} Вы определяете эту форму объявления переменной на глобальном уровне, в локальном объёме функции или в контексте объявления класса, структуры, перечисления или расширения. Когда объявление переменной в такой форме объявляется на глобальном уровне или в локальном объёме функции, оно называется вычисляемой переменной. Когда оно объявляется в контексте объявления класса, структуры или расширения, оно называется вычисляемым свойством.
Для чтения значения используется getter, а для записи — setter. Оператор setter необязателен, и если нужен только getter, вы можете опустить оба оператора и просто напрямую вернуть запрошенное значение, как описано в <doc:Properties#Read-Only-Computed-Properties>. Но если вы предоставите оператор setter, вы также должны предоставить оператор getter.
Имя setter и окружающие скобки необязательны. Если вы предоставите имя setter, оно будет использоваться как имя параметра для setter. Если вы не предоставите имя setter, имя параметра по умолчанию для setter — newValue, как описано в <doc:Properties#Shorthand-Setter-Declaration>.
В отличие от хранимых именованных значений и хранимых свойств переменных, значение вычисляемого именованного значения или вычисляемого свойства не хранится в памяти.
Дополнительную информацию и примеры вычисляемых свойств см. в <doc:Properties#Computed-Properties>.
Наблюдатели за хранимыми переменными и свойствами
Вы также можете объявить хранимую переменную или свойство с наблюдателями willSet и didSet. Хранимая переменная или свойство, объявленное с наблюдателями, имеет следующий вид:
var <#variable name#>: <#type#> = <#expression#> {
willSet(<#setter name#>) {
<#statements#>
}
didSet(<#setter name#>) {
<#statements#>
}
} Вы определяете эту форму объявления переменной на глобальном уровне, в локальном объёме функции или в контексте объявления класса или структуры. Когда объявление переменной в такой форме объявляется на глобальном уровне или в локальном объёме функции, наблюдатели называются наблюдателями за хранимыми переменными. Когда оно объявляется в контексте объявления класса или структуры, наблюдатели называются наблюдателями за свойствами.
Вы можете добавить наблюдатели свойств к любому хранимому свойству. Вы также можете добавить наблюдатели свойств к любому унаследованному свойству (хранимому или вычисляемому) путём переопределения свойства в подклассе, как описано в <doc:Inheritance#Overriding-Property-Observers>.
Инициализирующее выражение необязательно в контексте объявления класса или структуры, но обязательно в других случаях. Аннотация типа необязательна, если тип можно вывести из инициализирующего выражения. Это выражение вычисляется в первый раз, когда вы читаете значение свойства. Если вы перезаписываете начальное значение свойства без его чтения, это выражение вычисляется перед первым временем, когда вы записываете в свойство.
Наблюдатели willSet и didSet предоставляют способ наблюдения (и соответствующего реагирования) при установке значения переменной или свойства. Наблюдатели не вызываются при первой инициализации переменной или свойства. Вместо этого они вызываются только при установке значения вне контекста инициализации.
Наблюдатель willSet вызывается непосредственно перед установкой значения переменной или свойства. Новое значение передаётся наблюдателю willSet как константа, и поэтому оно не может быть изменено в реализации оператора willSet. Наблюдатель didSet вызывается сразу после установки нового значения. В отличие от наблюдателя willSet, старое значение переменной или свойства передаётся наблюдателю didSet, в случае необходимости доступа к нему. Однако, если вы присваиваете значение переменной или свойству в собственном операторе наблюдателя didSet, это новое присвоенное значение заменит то, которое только что было установлено и передано наблюдателю willSet.
Имя setter и окружающие скобки в операторах willSet и didSet необязательны. Если вы предоставите имена setter, они будут использоваться в качестве имён параметров наблюдателей willSet и didSet. Если вы не предоставите имена setter, имя параметра по умолчанию для наблюдателя willSet — newValue, а имя параметра по умолчанию для наблюдателя didSet — oldValue.
Оператор didSet необязателен при наличии оператора willSet. Аналогично, оператор willSet необязателен при наличии оператора didSet.
Если тело наблюдателя didSet ссылается на старое значение, getter вызывается до наблюдателя, чтобы сделать старое значение доступным. В противном случае новое значение хранится без вызова getter-метода родительского класса. Пример ниже демонстрирует вычисляемое свойство, которое определено родительским классом и переопределено его подклассами для добавления наблюдателя.
class Superclass {
private var xValue = 12
var x: Int {
get { print("Getter was called"); return xValue }
set { print("Setter was called"); xValue = newValue }
}
}
// This subclass doesn't refer to oldValue in its observer, so the
// superclass's getter is called only once to print the value.
class New: Superclass {
override var x: Int {
didSet { print("New value \(x)") }
}
}
let new = New()
new.x = 100
// Prints "Setter was called"
// Prints "Getter was called"
// Prints "New value 100"
// This subclass refers to oldValue in its observer, so the superclass's
// getter is called once before the setter, and again to print the value.
class NewAndOld: Superclass {
override var x: Int {
didSet { print("Old value \(oldValue) - new value \(x)") }
}
}
let newAndOld = NewAndOld()
newAndOld.x = 200
// Prints "Getter was called"
// Prints "Setter was called"
// Prints "Getter was called"
// Prints "Old value 12 - new value 200" Дополнительную информацию и примеры использования наблюдателей свойств см. в <doc:Properties#Property-Observers>.
Свойства переменных типов
Для объявления свойства переменного типа используйте модификатор объявления static. Классы могут использовать модификатор объявления class для вычисляемых свойств переменного типа, чтобы разрешить подклассам переопределять реализацию родительского класса. Свойства переменных типов обсуждаются в <doc:Properties#Type-Properties>.
Грамматика объявления переменной:
variable-declaration → variable-declaration-head pattern-initializer-list
variable-declaration → variable-declaration-head variable-name type-annotation code-block
variable-declaration → variable-declaration-head variable-name type-annotation getter-setter-block
variable-declaration → variable-declaration-head variable-name type-annotation getter-setter-keyword-block
variable-declaration → variable-declaration-head variable-name initializer willSet-didSet-block
variable-declaration → variable-declaration-head variable-name type-annotation initializer_?_ willSet-didSet-blockvariable-declaration-head → attributes_?_ declaration-modifiers_?_
var
variable-name → identifiergetter-setter-block → code-block
getter-setter-block →{getter-clause setter-clause_?_}
getter-setter-block →{setter-clause getter-clause}
getter-clause → attributes_?_ mutation-modifier_?_getcode-block
setter-clause → attributes_?_ mutation-modifier_?_setsetter-name_?_ code-block
setter-name →(identifier)getter-setter-keyword-block →
{getter-keyword-clause setter-keyword-clause_?_}
getter-setter-keyword-block →{setter-keyword-clause getter-keyword-clause}
getter-keyword-clause → attributes_?_ mutation-modifier_?_get
setter-keyword-clause → attributes_?_ mutation-modifier_?_setwillSet-didSet-block →
{willSet-clause didSet-clause_?_}
willSet-didSet-block →{didSet-clause willSet-clause_?_}
willSet-clause → attributes_?_willSetsetter-name_?_ code-block
didSet-clause → attributes_?_didSetsetter-name_?_ code-block
Объявление псевдонима типа
Объявление псевдонима типа вводит именованный псевдоним существующего типа в вашу программу. Объявления псевдонимов типов объявляются с помощью ключевого слова typealias и имеют следующий вид:
typealias <#name#> = <#existing type#>
После объявления псевдонима типа, алиасовое имя может использоваться вместо существующего типа в любой части вашей программы. Существующий тип может быть именованным типом или составным типом. Псевдонимы типов не создают новых типов; они просто позволяют имени ссылаться на существующий тип.
Объявление псевдонима типа может использовать параметры обобщения для присвоения имени существующему обобщённому типу. Псевдоним типа может предоставить конкретные типы для некоторых или всех параметров обобщения существующего типа. Например:
typealias StringDictionary<Value> = Dictionary<String, Value> // The following dictionaries have the same type. var dictionary1: StringDictionary<Int> = [:] var dictionary2: Dictionary<String, Int> = [:]
Когда псевдоним типа объявляется с параметрами обобщения, ограничения на эти параметры должны точно соответствовать ограничениям параметров обобщения существующего типа. Например:
typealias DictionaryOfInts<Key: Hashable> = Dictionary<Key, Int>
Поскольку псевдоним типа и существующий тип могут использоваться взаимозаменяемо, псевдоним типа не может вводить дополнительные ограничения обобщения.
Псевдоним типа может передавать параметры обобщения существующего типа, опуская все параметры обобщения из объявления. Например, псевдоним типа Diccionario, объявленный здесь, имеет те же параметры обобщения и ограничения, что и Dictionary.
typealias Diccionario = Dictionary
Внутри объявления протокола псевдоним типа может присвоить более короткое и удобное имя типу, который часто используется. Например:
protocol Sequence {
associatedtype Iterator: IteratorProtocol
typealias Element = Iterator.Element
}
func sum<T: Sequence>(_ sequence: T) -> Int where T.Element == Int {
// ...
} Без этого псевдонима типа, функция sum должна была бы ссылаться на ассоциированный тип как T.Iterator.Element вместо T.Element.
См. также <doc:Declarations#Protocol-Associated-Type-Declaration>.
Грамматика объявления псевдонима типа:
typealias-declaration → attributes_?_ access-level-modifier_?_
typealiastypealias-name generic-parameter-clause_?_ typealias-assignment
typealias-name → identifier
typealias-assignment →=type
Объявление функции
Объявление функции вводит функцию или метод в вашу программу. Функция, объявленная в контексте класса, структуры, перечисления или протокола, называется методом. Объявления функций объявляются с помощью ключевого слова func и имеют следующий вид:
func <#function name#>(<#parameters#>) -> <#return type#> {
<#statements#>
} Если функция имеет возвращаемый тип Void, тип возвращаемого значения можно опустить следующим образом:
func <#function name#>(<#parameters#>) {
<#statements#>
} Тип каждого параметра должен быть указан — его нельзя вывести. Если вы напишете inout перед типом параметра, параметр можно изменить внутри области видимости функции. Подробное описание параметров ввода-вывода приведено в <doc:Declarations#In-Out-Parameters> ниже.
Объявление функции, содержащее в качестве инструкций единственное выражение, понимается как возврат значения этого выражения. Этот синтаксис неявного возврата рассматривается только в том случае, если тип выражения и тип возвращаемого значения функции не Void и не являются перечислением, например, Never, не имеющим случаев.
Функции могут возвращать несколько значений с помощью кортежа типов в качестве типа возвращаемого значения функции.
Определение функции может находиться внутри другого объявления функции. Такая функция известна как вложенная функция.
Вложенная функция считается невыходящей за пределы области видимости (nonescaping), если она захватывает значение, которое гарантированно никогда не выйдет за пределы области видимости — например, параметр ввода-вывода — или передается как аргумент функции, не выходящей за пределы области видимости. В противном случае вложенная функция является выходящей за пределы области видимости (escaping) функцией.
Подробное описание вложенных функций см. в <doc:Functions#Nested-Functions>.
Имена параметров
Параметры функции представляют собой список, разделенный запятыми, где каждый параметр имеет одну из нескольких форм. Порядок аргументов в вызове функции должен соответствовать порядку параметров в объявлении функции. Самый простой элемент в списке параметров имеет следующий вид:
<#parameter name#>: <#parameter type#>
Параметр имеет имя, используемое внутри тела функции, а также метку аргумента, используемую при вызове функции или метода. По умолчанию имена параметров также используются в качестве меток аргументов. Например:
func f(x: Int, y: Int) -> Int { return x + y }
f(x: 1, y: 2) // both x and y are labeled Можно переопределить поведение по умолчанию для меток аргументов с помощью одной из следующих форм:
<#argument label#> <#parameter name#>: <#parameter type#> _ <#parameter name#>: <#parameter type#>
Имя перед именем параметра присваивает параметру явную метку аргумента, которая может отличаться от имени параметра. Соответствующий аргумент должен использовать указанную метку аргумента в вызовах функций или методов.
Подчеркивание (_) перед именем параметра подавляет метку аргумента. Соответствующий аргумент не должен иметь метки в вызовах функций или методов.
func repeatGreeting(_ greeting: String, count n: Int) { /* Greet n times */ }
repeatGreeting("Hello, world!", count: 2) // count is labeled, greeting is not Модификаторы параметров
Модификатор параметра изменяет способ передачи аргумента в функцию.
<#argument label#> <#parameter name#>: <#parameter modifier#> <#parameter type#>
Чтобы использовать модификатор параметра, запишите inout, borrowing или consuming перед типом аргумента.
func someFunction(a: inout A, b: consuming B, c: C) { ... } Параметры ввода-вывода
По умолчанию аргументы функций в Swift передаются по значению: любые изменения, внесенные внутри функции, не видны в вызывающей стороне. Чтобы сделать параметр ввода-вывода, используйте модификатор параметра inout.
func someFunction(a: inout Int) {
a += 1
} При вызове функции, содержащей параметры ввода-вывода, аргумент ввода-вывода должен быть префиксным амперсандом (&), чтобы указать, что вызов функции может изменить значение аргумента.
var x = 7 someFunction(&x) print(x) // Prints "8"
Параметры ввода-вывода передаются следующим образом:
- При вызове функции значение аргумента копируется.
- В теле функции копия модифицируется.
- При возврате из функции значение копии присваивается исходному аргументу.
Это поведение известно как копирование-вход, копирование-выход или вызов по значению с результатом. Например, при передаче вычисляемого свойства или свойства с наблюдателями в качестве параметра ввода-вывода, его getter вызывается в рамках вызова функции, а его setter вызывается в рамках возврата из функции.
В целях оптимизации, когда аргумент хранится по физическому адресу в памяти, используется то же место в памяти как внутри, так и вне тела функции. Оптимизированное поведение известно как вызов по ссылке; оно удовлетворяет всем требованиям модели копирование-вход, копирование-выход, одновременно удаляя издержки копирования. Пишите свой код, используя модель копирование-вход, копирование-выход, не полагаясь на оптимизацию вызова по ссылке, чтобы она работала правильно с оптимизацией или без нее.
Внутри функции не обращайтесь к значению, переданному в качестве аргумента ввода-вывода, даже если исходное значение доступно в текущей области видимости. Доступ к оригиналу — это одновременный доступ к значению, что нарушает исключительность доступа к памяти.
var someValue: Int
func someFunction(a: inout Int) {
a += someValue
}
// Error: This causes a runtime exclusivity violation
someFunction(&someValue) По той же причине вы не можете передать одно и то же значение нескольким параметрам ввода-вывода.
var someValue: Int
func someFunction(a: inout Int, b: inout Int) {
a += b
b += 1
}
// Error: Cannot pass the same value to multiple in-out parameters
someFunction(&someValue, &someValue) Дополнительную информацию о безопасности памяти и исключительности доступа к памяти см. в MemorySafety.
Закрытие или вложенная функция, которая захватывает параметр ввода-вывода, должна быть невыходящей за пределы области видимости. Если вам нужно захватить параметр ввода-вывода без его изменения, используйте список захвата, чтобы явно захватить параметр неизменяемо.
func someFunction(a: inout Int) -> () -> Int {
return { [a] in return a + 1 }
} Если вам нужно захватить и изменить параметр ввода-вывода, используйте явную локальную копию, например, в многопоточном коде, который гарантирует, что все изменения завершены до возврата из функции.
func multithreadedFunction(queue: DispatchQueue, x: inout Int) {
// Make a local copy and manually copy it back.
var localX = x
defer { x = localX }
// Operate on localX asynchronously, then wait before returning.
queue.async { someMutatingOperation(&localX) }
queue.sync {}
} Подробнее о параметрах ввода-вывода и примерах см. в <doc:Functions#In-Out-Parameters>.
Заимствование и потребление параметров
По умолчанию Swift использует набор правил для автоматического управления временем жизни объекта при вызовах функций, копируя значения при необходимости. Правила по умолчанию предназначены для минимизации накладных расходов в большинстве случаев — если вы хотите более точный контроль, вы можете применить модификатор параметра borrowing или consuming. В этом случае используйте copy для явного обозначения операций копирования.
Независимо от того, используете ли вы правила по умолчанию, Swift гарантирует, что время жизни и владение объектом правильно управляются во всех случаях. Эти модификаторы параметров влияют только на относительную эффективность определенных моделей использования, а не на правильность.
Модификатор borrowing указывает, что функция не сохраняет значение параметра. В этом случае вызывающая сторона сохраняет право собственности на объект и несет ответственность за время жизни объекта. Использование borrowing минимизирует накладные расходы, когда функция использует объект только временно.
// `isLessThan` does not keep either argument
func isLessThan(lhs: borrowing A, rhs: borrowing A) -> Bool {
...
} Если функция должна сохранить значение параметра, например, сохранив его в глобальной переменной — вы используете copy, чтобы явно скопировать это значение.
// As above, but this `isLessThan` also wants to record the smallest value
func isLessThan(lhs: borrowing A, rhs: borrowing A) -> Bool {
if lhs < storedValue {
storedValue = copy lhs
} else if rhs < storedValue {
storedValue = copy rhs
}
return lhs < rhs
} Напротив, модификатор параметра consuming указывает, что функция берет на себя владение значением, принимая на себя ответственность за его хранение или уничтожение до возврата из функции.
// `store` keeps its argument, so mark it `consuming`
func store(a: consuming A) {
someGlobalVariable = a
} Использование consuming минимизирует накладные расходы, когда вызывающая сторона больше не нуждается в использовании объекта после вызова функции.
// Usually, this is the last thing you do with a value store(a: value)
Если вы продолжаете использовать копируемый объект после вызова функции, компилятор автоматически создает копию этого объекта перед вызовом функции.
// The compiler inserts an implicit copy here store(a: someValue) // This function consumes someValue print(someValue) // This uses the copy of someValue
В отличие от inout, ни borrowing, ни consuming параметры не требуют какого-либо специального обозначения при вызове функции:
func someFunction(a: borrowing A, b: consuming B) { ... }
someFunction(a: someA, b: someB) Явное использование либо borrowing, либо consuming указывает ваше намерение более точно контролировать накладные расходы на управление временем жизни в runtime. Поскольку копии могут вызвать неожиданные операции владения в runtime, параметры, помеченные одним из этих модификаторов, не могут быть скопированы, если вы не используете явное ключевое слово copy:
func borrowingFunction1(a: borrowing A) {
// Error: Cannot implicitly copy a
// This assignment requires a copy because
// `a` is only borrowed from the caller.
someGlobalVariable = a
}
func borrowingFunction2(a: borrowing A) {
// OK: Explicit copying works
someGlobalVariable = copy a
}
func consumingFunction1(a: consuming A) {
// Error: Cannot implicitly copy a
// This assignment requires a copy because
// of the following `print`
someGlobalVariable = a
print(a)
}
func consumingFunction2(a: consuming A) {
// OK: Explicit copying works regardless
someGlobalVariable = copy a
print(a)
}
func consumingFunction3(a: consuming A) {
// OK: No copy needed here because this is the last use
someGlobalVariable = a
} Особые виды параметров
Параметры могут быть пропущены, принимать переменное количество значений и иметь значения по умолчанию, используя следующие формы:
_ : <#parameter type#> <#parameter name#>: <#parameter type#>... <#parameter name#>: <#parameter type#> = <#default argument value#>
Параметр с подчеркиванием (_) явно игнорируется и к нему нельзя получить доступ внутри тела функции.
Параметр с именем базового типа, за которым сразу следует три точки (...), понимается как параметр с переменным числом аргументов. Параметр, который следует непосредственно за параметром с переменным числом аргументов, должен иметь метку аргумента. Функция может иметь несколько параметров с переменным числом аргументов. Параметр с переменным числом аргументов обрабатывается как массив, содержащий элементы базового типа. Например, параметр с переменным числом аргументов Int... обрабатывается как [Int]. Пример использования параметра с переменным числом аргументов см. в <doc:Functions#Variadic-Parameters>.
Параметр со знаком равенства (=) и выражением после его типа понимается как имеющий значение по умолчанию, заданное данным выражением. Заданное выражение вычисляется при вызове функции. Если параметр опущен при вызове функции, используется значение по умолчанию.
func f(x: Int = 42) -> Int { return x }
f() // Valid, uses default value
f(x: 7) // Valid, uses the value provided
f(7) // Invalid, missing argument label Особые виды методов
Методы перечисления или структуры, которые изменяют self, должны быть помечены модификатором объявления mutating.
Методы, которые переопределяют метод суперкласса, должны быть помечены модификатором объявления override. Это ошибка времени компиляции, если метод переопределяется без модификатора override или используется модификатор override для метода, не переопределяющего метод суперкласса.
Методы, связанные с типом, а не экземпляром типа, должны быть помечены модификатором объявления static для перечислений и структур или модификатором объявления static или class для классов. Метод типа класса, помеченный модификатором объявления class, может быть переопределен реализацией подкласса; метод типа класса, помеченный class final или static, переопределяться не может.
Методы со специальными именами
Некоторые методы со специальными именами обеспечивают синтаксический сахар для синтаксиса вызова функции. Если тип определяет один из этих методов, экземпляры типа могут использоваться в синтаксисе вызова функции. Вызов функции понимается как вызов одного из методов со специальными именами для этого экземпляра.
Тип класса, структуры или перечисления может поддерживать синтаксис вызова функции путем определения метода dynamicallyCall(withArguments:) или метода dynamicallyCall(withKeywordArguments:), как описано в <doc:Attributes#dynamicCallable>, или путем определения метода вызова как функции, как описано ниже. Если тип определяет как метод вызова как функцию, так и один из методов, используемых атрибутом dynamicCallable, компилятор отдает предпочтение методу вызова как функции в тех случаях, когда может быть использован любой из этих методов.
Имя метода вызова как функции — callAsFunction() или другое имя, начинающееся с callAsFunction( и добавляющее помеченные или непомеченные аргументы — например, callAsFunction(_:_:) и callAsFunction(something:) также являются допустимыми именами методов вызова как функции.
Следующие вызовы функций эквивалентны:
struct CallableStruct {
var value: Int
func callAsFunction(_ number: Int, scale: Int) {
print(scale * (number + value))
}
}
let callable = CallableStruct(value: 100)
callable(4, scale: 2)
callable.callAsFunction(4, scale: 2)
// Both function calls print 208. Методы вызова-как-функции и методы из атрибута dynamicCallable предоставляют разные компромиссы между объёмом информации, кодируемой в системе типов, и динамическим поведением, возможным во время выполнения. При объявлении метода вызова-как-функции вы указываете количество аргументов, а также тип и метку каждого аргумента. Методы атрибута dynamicCallable указывают только тип, используемый для хранения массива аргументов.
Определение метода вызова-как-функции или метода из атрибута dynamicCallable не позволяет использовать экземпляр этого типа как функцию в любом контексте, кроме выражения вызова функции. Например:
let someFunction1: (Int, Int) -> Void = callable(_:scale:) // Error let someFunction2: (Int, Int) -> Void = callable.callAsFunction(_:scale:)
Индекс subscript(dynamicMember:) обеспечивает синтаксический сахар для поиска членов, как описано в <doc:Attributes#dynamicMemberLookup>.
Функции и методы, бросающие исключения
Функции и методы, которые могут бросить исключение, должны быть помечены ключевым словом throws. Эти функции и методы известны как функции, бросающие исключения и методы, бросающие исключения. Они имеют следующий вид:
func <#function name#>(<#parameters#>) throws -> <#return type#> {
<#statements#>
} Функция, бросающая исключение определённого типа, имеет следующий вид:
func <#function name#>(<#parameters#>) throws(<#error type#>) -> <#return type#> {
<#statements#>
} Вызовы функции или метода, бросающих исключение, должны быть заключены в выражение try или try! (то есть, в области действия оператора try или try!).
Тип функции включает в себя возможность бросания исключений и тип бросаемого исключения. Это отношение подтипов означает, например, что вы можете использовать функцию, не бросающую исключения, в контексте, где ожидается функция, бросающая исключения. Для получения дополнительной информации о типе функции, бросающей исключение, см. <doc:Types#Function-Type>. Примеры работы с ошибками, имеющими явные типы, см. в <doc:ErrorHandling#Specifying-the-Error-Type>.
Перегрузить функцию только на основе того, может ли функция бросать исключение, нельзя. Однако, вы можете перегрузить функцию на основе того, может ли параметр функции бросать исключение.
Метод, бросающий исключение, не может переопределять метод, не бросающий исключение, и метод, бросающий исключение, не может удовлетворять требованию протокола для метода, не бросающего исключение. При этом метод, не бросающий исключение, может переопределять метод, бросающий исключение, и метод, не бросающий исключение, может удовлетворять требованию протокола для метода, бросающего исключение.
Функции и методы, перебрасывающие исключения
Функция или метод могут быть объявлены с ключевым словом rethrows, чтобы указать, что он бросает исключение только если один из его параметров-функций бросает исключение. Эти функции и методы известны как функции, перебрасывающие исключения и методы, перебрасывающие исключения. Функции и методы, перебрасывающие исключения, должны иметь как минимум один параметр-функцию, бросающий исключение.
func someFunction(callback: () throws -> Void) rethrows {
try callback()
} Функция или метод, перебрасывающий исключение, может содержать оператор throw только внутри блока catch. Это позволяет вызвать бросающую исключение функцию внутри оператора do-catch и обрабатывать исключения в блоке catch, бросая другое исключение. Кроме того, блок catch должен обрабатывать только исключения, сгенерированные одним из бросающих исключение параметров функции перебрасывания. Например, следующее некорректно, потому что блок catch будет обрабатывать исключение, сгенерированное alwaysThrows().
func alwaysThrows() throws {
throw SomeError.error
}
func someFunction(callback: () throws -> Void) rethrows {
do {
try callback()
try alwaysThrows() // Invalid, alwaysThrows() isn't a throwing parameter
} catch {
throw AnotherError.error
}
} Метод, бросающий исключение, не может переопределять метод, перебрасывающий исключения, и метод, бросающий исключение, не может удовлетворять требованию протокола для метода, перебрасывающего исключения. При этом метод, перебрасывающий исключения, может переопределять метод, бросающий исключение, и метод, перебрасывающий исключения, может удовлетворять требованию протокола для метода, бросающего исключение.
Альтернативой перебрасыванию является бросание исключения определённого типа в обобщённом коде. Например:
func someFunction<E: Error>(callback: () throws(E) -> Void) throws(E) {
try callback()
} Этот подход к распространению исключения сохраняет информацию о типе исключения. Однако, в отличие от пометки функции rethrows, этот подход не предотвращает функцию от бросания исключения того же типа.
Асинхронные функции и методы
Функции и методы, выполняемые асинхронно, должны быть помечены ключевым словом async. Эти функции и методы известны как асинхронные функции и асинхронные методы. Они имеют следующий вид:
func <#function name#>(<#parameters#>) async -> <#return type#> {
<#statements#>
} Вызовы асинхронной функции или метода должны быть заключены в выражение await — то есть, они должны находиться в области действия оператора await.
Ключевое слово async является частью типа функции, а синхронные функции являются подтипами асинхронных функций. В результате вы можете использовать синхронную функцию в контексте, где ожидается асинхронная функция. Например, вы можете переопределить асинхронный метод синхронным методом, и синхронный метод может удовлетворять требованию протокола, требующему асинхронный метод.
Вы можете перегрузить функцию в зависимости от того, является ли функция асинхронной или нет. В месте вызова контекст определяет, какая перегрузка используется: в асинхронном контексте используется асинхронная функция, а в синхронном — синхронная.
Асинхронный метод не может переопределять синхронный метод, и асинхронный метод не может удовлетворять требованию протокола для синхронного метода. При этом синхронный метод может переопределять асинхронный метод, и синхронный метод может удовлетворять требованию протокола для асинхронного метода.
Функции, которые никогда не возвращаются
Swift определяет тип Never, который указывает, что функция или метод не возвращаются своему вызывающему объекту. Функции и методы с типом возврата Never называются невозвращаемыми. Невозвращаемые функции и методы либо вызывают невосстановимую ошибку, либо начинают последовательность работы, которая продолжается бесконечно. Это означает, что код, который в противном случае выполнялся бы сразу после вызова, никогда не выполняется. Бросающие и перебрасывающие исключения функции могут передать управление программе в соответствующий блок catch, даже когда они являются невозвращаемыми.
Невозвращаемую функцию или метод можно вызвать для завершения блока else оператора guard, как описано в <doc:Statements#Guard-Statement>.
Вы можете переопределить невозвращаемый метод, но новый метод должен сохранить свой тип возврата и поведение невозвращения.
Грамматика объявления функции:
function-declaration → function-head function-name generic-parameter-clause_?_ function-signature generic-where-clause_?_ function-body_?_
function-head → attributes_?_ declaration-modifiers_?_
func
function-name → identifier | operatorfunction-signature → parameter-clause
async_?_ throws-clause_?_ function-result_?_
function-signature → parameter-clauseasync_?_rethrowsfunction-result_?_
function-result →->attributes_?_ type
function-body → code-blockparameter-clause →
()|(parameter-list)
parameter-list → parameter | parameter,parameter-list
parameter → external-parameter-name_?_ local-parameter-name parameter-type-annotation default-argument-clause_?_
parameter → external-parameter-name_?_ local-parameter-name parameter-type-annotation
parameter → external-parameter-name_?_ local-parameter-name parameter-type-annotation...external-parameter-name → identifier
local-parameter-name → identifier
parameter-type-annotation →:attributes_?_ parameter-modifier_?_ type
parameter-modifier →inout|borrowing|consumingdefault-argument-clause →=expression
Объявление перечисления
Объявление перечисления вводит именованный тип перечисления в вашу программу.
Объявления перечислений имеют две основные формы и объявляются с помощью ключевого слова enum. Тело перечисления, объявленного с помощью любой из этих форм, содержит ноль или более значений — называемых случаями перечисления — и любое количество объявлений, включая вычисляемые свойства, методы экземпляров, статические методы, инициализаторы, псевдонимы типов и даже другие объявления перечислений, структур, классов и акторов. Объявления перечислений не могут содержать деинициализаторы или объявления протоколов.
Типы перечислений могут реализовывать любое количество протоколов, но не могут наследоваться от классов, структур или других перечислений.
В отличие от классов и структур, у типов перечислений нет неявного инициализатора по умолчанию; все инициализаторы должны быть объявлены явно. Инициализаторы могут делегировать другим инициализаторам в перечислении, но процесс инициализации завершается только после того, как инициализатор присвоит одно из случаев перечисления значению self.
Подобно структурам, но в отличие от классов, перечисления являются типами значений; экземпляры перечисления копируются при присваивании переменным или константам, или при передаче в качестве аргументов функции. Сведения о типах значений см. в <doc:ClassesAndStructures#Structures-and-Enumerations-Are-Value-Types>.
Вы можете расширить поведение типа перечисления с помощью объявления расширения, как описано в <doc:Declarations#Extension-Declaration>.
Перечисления со случаями любого типа
Следующая форма объявляет тип перечисления, содержащий случаи перечисления любого типа:
enum <#enumeration name#>: <#adopted protocols#> {
case <#enumeration case 1#>
case <#enumeration case 2#>(<#associated value types#>)
} Перечисления, объявленные в этой форме, иногда называются дискриминированными союзами в других языках программирования.
В этой форме каждый блок case состоит из ключевого слова case, за которым следуют один или несколько случаев перечисления, разделённых запятыми. Имя каждого случая должно быть уникальным. Каждый случай также может указать, что он хранит значения заданного типа. Эти типы указываются в кортеже типов ассоциированных значений, сразу после имени случая.
Случаи перечисления, хранящие ассоциированные значения, могут использоваться как функции, которые создают экземпляры перечисления с указанными ассоциированными значениями. И, как и функции, вы можете получить ссылку на случай перечисления и применить его позже в вашем коде.
enum Number {
case integer(Int)
case real(Double)
}
let f = Number.integer
// f is a function of type (Int) -> Number
// Apply f to create an array of Number instances with integer values
let evenInts: [Number] = [0, 2, 4, 6].map(f) Для получения дополнительной информации и просмотра примеров случаев с соответствующими типами значений, см. <doc:Перечисления#Связанные-значения>.
Перечисления с косвенным доступом
Перечисления могут иметь рекурсивную структуру, то есть они могут содержать случаи со связанными значениями, которые являются экземплярами самого типа перечисления. Однако экземпляры типов перечисления имеют семантику значений, что означает, что они имеют фиксированную структуру в памяти. Для поддержки рекурсии компилятор должен вставить уровень косвенного доступа.
Чтобы включить косвенный доступ для конкретного случая перечисления, пометьте его модификатором объявления indirect. Случай с косвенным доступом должен иметь связанное значение.
enum Tree<T> {
case empty
indirect case node(value: T, left: Tree, right: Tree)
} Чтобы включить косвенный доступ для всех случаев перечисления, имеющих связанное значение, пометьте всё перечисление модификатором indirect — это удобно, когда перечисление содержит много случаев, которые каждый нужно было бы помечать модификатором indirect.
Перечисление, помеченное модификатором indirect, может содержать смесь случаев со связанными значениями и случаев без них. Однако оно не может содержать случаев, также помеченных модификатором indirect.
Перечисления со случаями типа «сырое значение»
Следующая форма объявляет тип перечисления, содержащий случаи перечисления одного и того же базового типа:
enum <#enumeration name#>: <#raw-value type#>, <#adopted protocols#> {
case <#enumeration case 1#> = <#raw value 1#>
case <#enumeration case 2#> = <#raw value 2#>
} В этой форме каждый блок случая состоит из ключевого слова case, за которым следует один или несколько случаев перечисления, разделенных запятыми. В отличие от случаев в первой форме, каждый случай имеет базовое значение, называемое сырым значением, того же базового типа. Тип этих значений задается в типе сырого значения и должен представлять целое число, число с плавающей точкой, строку или один символ. В частности, тип сырого значения должен соответствовать протоколу Equatable и одному из следующих протоколов: ExpressibleByIntegerLiteral для целочисленных литералов, ExpressibleByFloatLiteral для литералов с плавающей точкой, ExpressibleByStringLiteral для строковых литералов, содержащих любое количество символов, и ExpressibleByUnicodeScalarLiteral или ExpressibleByExtendedGraphemeClusterLiteral для строковых литералов, содержащих только один символ. Каждый случай должен иметь уникальное имя и быть присвоен уникальное сырое значение.
Если тип сырого значения задан как Int и вы не присваиваете значения случаям явно, они неявно присваиваются значения 0, 1, 2 и так далее. Каждый неназначенный случай типа Int неявно получает сырое значение, которое автоматически увеличивается на единицу от сырого значения предыдущего случая.
enum ExampleEnum: Int {
case a, b, c = 5, d
} В приведенном выше примере сырое значение ExampleEnum.a равно 0, а значение ExampleEnum.b равно 1. И поскольку значение ExampleEnum.c явно установлено в 5, значение ExampleEnum.d автоматически увеличивается с 5 и поэтому равно 6.
Если тип сырого значения указан как String, и вы не присваиваете значения случаям явно, каждый неназначенный случай неявно получает строку с тем же текстом, что и имя этого случая.
enum GamePlayMode: String {
case cooperative, individual, competitive
} В приведенном выше примере сырое значение GamePlayMode.cooperative равно "cooperative", сырое значение GamePlayMode.individual равно "individual", а сырое значение GamePlayMode.competitive равно "competitive".
Перечисления со случаями типа «сырое значение» неявно соответствуют протоколу RawRepresentable, определенному в стандартной библиотеке Swift. В результате у них есть свойство rawValue и инициализатор с ошибкой, имеющий сигнатуру init?(rawValue: RawValue). Вы можете использовать свойство rawValue для доступа к сырому значению случая перечисления, как в ExampleEnum.b.rawValue. Вы также можете использовать сырое значение для поиска соответствующего случая, если он есть, вызвав инициализатор перечисления с ошибкой, как в ExampleEnum(rawValue: 5), который возвращает необязательный случай. Для получения дополнительной информации и просмотра примеров случаев с типами «сырое значение», см. <doc:Перечисления#Сырые-значения>.
Доступ к случаям перечисления
Для ссылки на случай типа перечисления используйте синтаксис точки (.), как в EnumerationType.enumerationCase. Когда тип перечисления можно определить из контекста, вы можете его опустить (точка всё равно необходима), как описано в <doc:Перечисления#Синтаксис-перечисления> и <doc:Выражения#Неявное-выражение-члена>.
Для проверки значений случаев перечисления используйте оператор switch, как показано в <doc:Перечисления#Сопоставление-значений-перечисления-с-оператором-switch>. Тип перечисления сопоставляется с шаблонами случаев перечисления в блоках случаев оператора switch, как описано в <doc:Шаблоны#Шаблон-случая-перечисления>.
Грамматика объявления перечисления:
enum-declaration → attributes_?_ access-level-modifier_?_ union-style-enum
enum-declaration → attributes_?_ access-level-modifier_?_ raw-value-style-enumunion-style-enum →
indirect_?_enumenum-name generic-parameter-clause_?_ type-inheritance-clause_?_ generic-where-clause_?_{union-style-enum-members_?_}
union-style-enum-members → union-style-enum-member union-style-enum-members_?_
union-style-enum-member → declaration | union-style-enum-case-clause | compiler-control-statement
union-style-enum-case-clause → attributes_?_indirect_?_caseunion-style-enum-case-list
union-style-enum-case-list → union-style-enum-case | union-style-enum-case,union-style-enum-case-list
union-style-enum-case → enum-case-name tuple-type_?_
enum-name → identifier
enum-case-name → identifierraw-value-style-enum →
enumenum-name generic-parameter-clause_?_ type-inheritance-clause generic-where-clause_?_{raw-value-style-enum-members}
raw-value-style-enum-members → raw-value-style-enum-member raw-value-style-enum-members_?_
raw-value-style-enum-member → declaration | raw-value-style-enum-case-clause | compiler-control-statement
raw-value-style-enum-case-clause → attributes_?_caseraw-value-style-enum-case-list
raw-value-style-enum-case-list → raw-value-style-enum-case | raw-value-style-enum-case,raw-value-style-enum-case-list
raw-value-style-enum-case → enum-case-name raw-value-assignment_?_
raw-value-assignment →=raw-value-literal
raw-value-literal → numeric-literal | static-string-literal | boolean-literal
Объявление структуры
Объявление структуры вводит именованный тип структуры в вашу программу. Объявления структур объявляются с помощью ключевого слова struct и имеют следующий вид:
struct <#structure name#>: <#adopted protocols#> {
<#declarations#>
} Тело структуры содержит ноль или более объявлений. Эти объявления могут включать как хранимые, так и вычисляемые свойства, свойства типа, методы экземпляра, методы типа, инициализаторы, индексы, псевдонимы типов и даже другие объявления структур, классов, акторов и перечислений. Объявления структур не могут содержать объявления деинициализатора или протокола. Для обсуждения и нескольких примеров структур, которые включают различные типы объявлений, см. ClassesAndStructures.
Типы структур могут реализовывать любое количество протоколов, но не могут наследоваться от классов, перечислений или других структур.
Существует три способа создания экземпляра ранее объявленной структуры:
- Вызов одного из инициализаторов, объявленных в структуре, как описано в <doc:Инициализация#Инициализаторы>.
- Если инициализаторы не объявлены, вызов инициализатора по умолчанию для членов структуры, как описано в <doc:Инициализация#Инициализаторы-по-членам-для-типов-структуры>.
- Если инициализаторы не объявлены, и всем свойствам структуры в объявлении были заданы начальные значения, вызов инициализатора по умолчанию, как описано в <doc:Инициализация#Инициализаторы-по-умолчанию>.
Процесс инициализации объявленных свойств структуры описан в Initialization.
Свойства экземпляра структуры можно получить с помощью синтаксиса точки (.), как описано в <doc:КлассыИСтруктуры#Доступ-к-свойствам>.
Структуры — это типы значений; экземпляры структуры копируются при присваивании переменным или константам или при передаче в качестве аргументов вызова функции. Подробнее о типах значений см. в <doc:КлассыИСтруктуры#Структуры-и-перечисления-являются-типами-значений>.
Вы можете расширить поведение типа структуры с помощью объявления расширения, как описано в <doc:Объявления#Объявление-расширения>.
Грамматика объявления структуры:
struct-declaration → attributes_?_ access-level-modifier_?_
structstruct-name generic-parameter-clause_?_ type-inheritance-clause_?_ generic-where-clause_?_ struct-body
struct-name → identifier
struct-body →{struct-members_?_}struct-members → struct-member struct-members_?_
struct-member → declaration | compiler-control-statement
Объявление класса
Объявление класса вводит именованный тип класса в вашу программу. Объявления классов объявляются с помощью ключевого слова class и имеют следующий вид:
class <#class name#>: <#superclass#>, <#adopted protocols#> {
<#declarations#>
} Тело класса содержит ноль или более объявлений. Эти объявления могут включать как хранимые, так и вычисляемые свойства, методы экземпляра, методы типа, инициализаторы, один деинициализатор, индексы, псевдонимы типов и даже объявления других классов, структур, акторов и перечислений. Объявления классов не могут содержать объявления протоколов. Для обсуждения и нескольких примеров классов, включающих различные типы объявлений, см. ClassesAndStructures.
Тип класса может наследоваться только от одного родительского класса, его суперкласса, но может принимать любое количество протоколов. Суперкласс указывается первым после имени класса и двоеточия, за которым следуют любые принятые протоколы. Обобщенные классы могут наследоваться от других обобщенных и необобщенных классов, но необобщенный класс может наследоваться только от других необобщенных классов. Когда вы пишете имя обобщенного суперкласса после двоеточия, вы должны указать полное имя этого обобщенного класса, включая его секцию параметров обобщения.
Как обсуждается в <doc:Declarations#Initializer-Declaration>, классы могут иметь назначенные и удобные инициализаторы. Назначенный инициализатор класса должен инициализировать все объявленные свойства класса, и он должен сделать это перед вызовом любых назначенных инициализаторов своего суперкласса.
Класс может переопределять свойства, методы, индексы и инициализаторы своего суперкласса. Переопределенные свойства, методы, индексы и назначенные инициализаторы должны быть помечены модификатором объявления override.
Чтобы потребовать, чтобы подклассы реализовывали инициализатор суперкласса, пометьте инициализатор суперкласса модификатором объявления required. Реализация этого инициализатора в подклассе также должна быть помечена модификатором required.
Хотя свойства и методы, объявленные в суперклассе, наследуются текущим классом, назначенные инициализаторы, объявленные в суперклассе, наследуются только тогда, когда подкласс удовлетворяет условиям, описанным в <doc:Initialization#Automatic-Initializer-Inheritance>. Swift-классы не наследуются от универсального базового класса.
Существует два способа создания экземпляра ранее объявленного класса:
- Вызовите один из инициализаторов, объявленных в классе, как описано в <doc:Initialization#Initializers>.
- Если инициализаторы не объявлены, а всем свойствам объявления класса были присвоены начальные значения, вызовите стандартный инициализатор класса, как описано в <doc:Initialization#Default-Initializers>.
Доступ к свойствам экземпляра класса можно получить с помощью синтаксиса точки (.), как описано в <doc:ClassesAndStructures#Accessing-Properties>.
Классы являются ссылками; экземпляры класса ссылаются, а не копируются, при назначении переменным или константам или при передаче в качестве аргументов в вызов функции. Сведения о типах ссылок см. в <doc:ClassesAndStructures#Classes-Are-Reference-Types>.
Вы можете расширить поведение типа класса с помощью объявления расширения, как обсуждается в <doc:Declarations#Extension-Declaration>.
Грамматика объявления класса:
class-declaration → attributes_?_ access-level-modifier_?_
final_?_classclass-name generic-parameter-clause_?_ type-inheritance-clause_?_ generic-where-clause_?_ class-body
class-declaration → attributes_?_finalaccess-level-modifier_?_classclass-name generic-parameter-clause_?_ type-inheritance-clause_?_ generic-where-clause_?_ class-body
class-name → identifier
class-body →{class-members_?_}class-members → class-member class-members_?_
class-member → declaration | compiler-control-statement
Объявление актора
Объявление актора вводит именованный тип актора в вашу программу. Объявления акторов объявляются с помощью ключевого слова actor и имеют следующий вид:
actor <#actor name#>: <#adopted protocols#> {
<#declarations#>
} Тело актора содержит ноль или более объявлений. Эти объявления могут включать как хранимые, так и вычисляемые свойства, методы экземпляра, методы типа, инициализаторы, один деинициализатор, индексы, псевдонимы типов и даже объявления других классов, структур и перечислений. Для обсуждения и нескольких примеров акторов, включающих различные типы объявлений, см. <doc:Concurrency#Actors>.
Типы акторов могут принимать любое количество протоколов, но не могут наследоваться от классов, перечислений, структур или других акторов. Однако актер, помеченный атрибутом @objc, неявно соответствует протоколу NSObjectProtocol и отображается в среде выполнения Objective-C как подтип NSObject.
Существует два способа создания экземпляра ранее объявленного актора:
- Вызовите один из инициализаторов, объявленных в акторе, как описано в <doc:Initialization#Initializers>.
- Если инициализаторы не объявлены, а всем свойствам объявления актора были присвоены начальные значения, вызовите стандартный инициализатор актора, как описано в <doc:Initialization#Default-Initializers>.
По умолчанию члены актора изолированы от этого актора. Код, такой как тело метода или getter свойства, выполняется в этом акторе. Код внутри актора может взаимодействовать с ними синхронно, потому что этот код уже выполняется в том же акторе, но код вне актора должен помечать их await, чтобы указать, что этот код асинхронно выполняется в другом акторе. Ключевые пути не могут ссылаться на изолированные члены актора. Изолированные хранимые свойства актора могут передаваться в качестве параметров in-out синхронным функциям, но не асинхронным функциям.
Акторы также могут иметь неизолированные члены, объявления которых помечены ключевым словом nonisolated. Неизолированный член выполняется как код вне актора: он не может взаимодействовать с каким-либо изолированным состоянием актора, и вызывающие стороны не помечают его await при использовании.
Члены актора могут быть помечены атрибутом @objc только если они неизолированы или асинхронны.
Процесс инициализации объявленных свойств актора описан в Initialization.
Свойства экземпляра актора можно получить с помощью синтаксиса точки (.), как описано в <doc:ClassesAndStructures#Accessing-Properties>.
Акторы являются ссылками; экземпляры актора ссылаются, а не копируются, при назначении переменным или константам или при передаче в качестве аргументов в вызов функции. Сведения о типах ссылок см. в <doc:ClassesAndStructures#Classes-Are-Reference-Types>.
Вы можете расширить поведение типа актора с помощью объявления расширения, как обсуждается в <doc:Declarations#Extension-Declaration>.
Грамматика объявления актора:
actor-declaration → attributes_?_ access-level-modifier_?_
actoractor-name generic-parameter-clause_?_ type-inheritance-clause_?_ generic-where-clause_?_ actor-body
actor-name → identifier
actor-body →{actor-members_?_}actor-members → actor-member actor-members_?_
actor-member → declaration | compiler-control-statement
Объявление протокола
Объявление протокола вводит именованный тип протокола в вашу программу. Объявления протоколов объявляются с помощью ключевого слова protocol и имеют следующий вид:
protocol <#protocol name#>: <#inherited protocols#> {
<#protocol member declarations#>
} Объявления протоколов могут появляться в глобальной области или внутри необобщенного типа или необобщенной функции.
Тело протокола содержит ноль или более объявлений членов протокола, которые описывают требования к соответствию, которым должен удовлетворять любой тип, принимающий протокол. В частности, протокол может объявлять, что соответствующие типы должны реализовывать определенные свойства, методы, инициализаторы и индексы. Протоколы также могут объявлять специальные типы псевдонимов, называемые связанными типами, которые могут задавать отношения между различными объявлениями протокола. Объявления протоколов не могут содержать объявления классов, структур, перечислений или других протоколов. Объявления членов протокола подробно обсуждаются ниже.
Типы протоколов могут наследоваться от любого количества других протоколов. Когда тип протокола наследуется от других протоколов, набор требований от этих других протоколов агрегируется, и любой тип, который наследуется от текущего протокола, должен соответствовать всем этим требованиям. Пример использования наследования протоколов см. в <doc:Protocols#Protocol-Inheritance>.
Примечание: вы также можете агрегировать требования соответствия нескольких протоколов с помощью типов композиции протоколов, как описано в <doc:Types#Protocol-Composition-Type> и <doc:Protocols#Protocol-Composition>.
Вы можете добавить соответствие протокола к ранее объявленному типу, приняв протокол в объявлении расширения этого типа. В расширении вы должны реализовать все требования принятого протокола. Если тип уже реализует все требования, вы можете оставить тело объявления расширения пустым.
По умолчанию типы, соответствующие протоколу, должны реализовывать все свойства, методы и индексы, объявленные в протоколе. Однако вы можете отметить эти объявления членов протокола модификатором объявления optional, чтобы указать, что их реализация соответствующим типом является необязательной. Модификатор optional может применяться только к членам, помеченным атрибутом objc, и только к членам протоколов, помеченных атрибутом objc. В результате только типы классов могут принимать и соответствовать протоколу, который содержит необязательные требования к членам. Более подробную информацию о том, как использовать модификатор объявления optional, и рекомендации по доступу к необязательным членам протоколов — например, когда вы не уверены, реализует ли их соответствующий тип — см. <doc:Protocols#Optional-Protocol-Requirements>.
Случаи перечисления могут удовлетворять требованиям протокола для членов типа. В частности, перечисление без сопутствующих значений удовлетворяет требованию протокола для только-для-чтения переменной типа Self, а перечисление сопутствующих значений удовлетворяет требованию протокола для функции, которая возвращает Self, параметры и их метки аргументов совпадают сопутствующими значениями случая. Например:
protocol SomeProtocol {
static var someValue: Self { get }
static func someFunction(x: Int) -> Self
}
enum MyEnum: SomeProtocol {
case someValue
case someFunction(x: Int)
}Чтобы ограничить применение протокола только для типов классов, включите протокол AnyObject в список наследуемых протоколов после двоеточия. Например, следующий протокол может быть принят только типами классов:
protocol SomeProtocol: AnyObject {
/* Protocol members go here */
} Любой протокол, который наследуется от протокола, помеченного требованием AnyObject, также может быть принят только типами классов.
Примечание: если протокол помечен атрибутом
objc, требованиеAnyObjectподразумевается для этого протокола; нет необходимости явно отмечать протокол требованиемAnyObject.
Протоколы — это именованные типы, и поэтому они могут появляться во всех тех же местах в вашем коде, что и другие именованные типы, как описано в <doc:Protocols#Protocols-as-Types>. Однако вы не можете создать экземпляр протокола, потому что протоколы фактически не предоставляют реализации требований, которые они задают.
Вы можете использовать протоколы для объявления методов, которые должен реализовать делегат класса или структуры, как описано в <doc:Protocols#Delegation>.
Грамматика объявления протокола:
protocol-declaration → attributes_?_ access-level-modifier_?_
protocolprotocol-name type-inheritance-clause_?_ generic-where-clause_?_ protocol-body
protocol-name → identifier
protocol-body →{protocol-members_?_}protocol-members → protocol-member protocol-members_?_
protocol-member → protocol-member-declaration | compiler-control-statementprotocol-member-declaration → protocol-property-declaration
protocol-member-declaration → protocol-method-declaration
protocol-member-declaration → protocol-initializer-declaration
protocol-member-declaration → protocol-subscript-declaration
protocol-member-declaration → protocol-associated-type-declaration
protocol-member-declaration → typealias-declaration
Объявление свойства протокола
Протоколы объявляют, что соответствующие типы должны реализовать свойство, включив объявление свойства протокола в тело объявления протокола. Объявления свойств протоколов имеют специальную форму объявления переменной:
var <#property name#>: <#type#> { get set } Как и другие объявления членов протокола, эти объявления свойств объявляют только требования к геттеру и сеттеру для типов, которые соответствуют протоколу. В результате вы не реализуете геттер или сеттер непосредственно в протоколе, в котором он объявлен.
Требования к геттеру и сеттеру могут быть удовлетворены соответствующим типом различными способами. Если объявление свойства включает ключевые слова get и set, соответствующий тип может реализовать его с помощью хранимой переменной или вычислимого свойства, которое одновременно читаемое и записываемое (то есть такое, которое реализует как геттер, так и сеттер). Однако это объявление свойства не может быть реализовано как постоянное свойство или как только для чтения вычислимое свойство. Если объявление свойства включает только ключевое слово get, оно может быть реализовано как любой тип свойства. Примеры соответствующих типов, которые реализуют требования к свойству протокола, см. в <doc:Protocols#Property-Requirements>.
Чтобы объявить требование свойства типа в объявлении протокола, отметьте объявление свойства ключевым словом static. Структуры и перечисления, которые соответствуют протоколу, объявляют свойство с ключевым словом static, а классы, которые соответствуют протоколу, объявляют свойство с ключевым словом static или class. Расширения, добавляющие соответствие протоколу к структуре, перечислению или классу, используют то же ключевое слово, что и использует тип, к которому они относятся. Расширения, которые предоставляют реализацию по умолчанию для требования свойства типа, используют ключевое слово static.
См. также <doc:Declarations#Variable-Declaration>.
Грамматика объявления свойства протокола:
protocol-property-declaration → variable-declaration-head variable-name type-annotation getter-setter-keyword-block
Объявление метода протокола
Протоколы объявляют, что соответствующие типы должны реализовать метод, включив объявление метода протокола в тело объявления протокола. Объявления методов протоколов имеют ту же форму, что и объявления функций, за двумя исключениями: они не содержат тела функции, и вы не можете указать значения параметров по умолчанию в рамках объявления функции. Примеры соответствующих типов, которые реализуют требования к методу протокола, см. в <doc:Protocols#Method-Requirements>.
Чтобы объявить требование метода класса или статического метода в объявлении протокола, отметьте объявление метода модификатором объявления static. Структуры и перечисления, которые соответствуют протоколу, объявляют метод с ключевым словом static, а классы, которые соответствуют протоколу, объявляют метод с ключевым словом static или class. Расширения, добавляющие соответствие протоколу к структуре, перечислению или классу, используют то же ключевое слово, что и использует тип, к которому они относятся. Расширения, которые предоставляют реализацию по умолчанию для требования метода типа, используют ключевое слово static.
См. также <doc:Declarations#Function-Declaration>.
Грамматика объявления метода протокола:
protocol-method-declaration → function-head function-name generic-parameter-clause_?_ function-signature generic-where-clause_?_
Объявление инициализатора протокола
Протоколы объявляют, что соответствующие типы должны реализовать инициализатор, включив объявление инициализатора протокола в тело объявления протокола. Объявления инициализаторов протоколов имеют ту же форму, что и объявления инициализаторов, за исключением того, что они не включают тело инициализатора.
Соответствующий тип может удовлетворить требованию инициализатора протокола без возможности отказа, реализовав инициализатор без возможности отказа или инициализатор init! с возможностью отказа. Соответствующий тип может удовлетворить требованию инициализатора протокола с возможностью отказа, реализовав любой тип инициализатора.
Когда класс реализует инициализатор для удовлетворения требования инициализатора протокола, инициализатор должен быть помечен модификатором объявления required, если класс уже не помечен модификатором объявления final.
См. также <doc:Declarations#Initializer-Declaration>.
Грамматика объявления инициализатора протокола:
protocol-initializer-declaration → initializer-head generic-parameter-clause_?_ parameter-clause throws-clause_?_ generic-where-clause_?_
protocol-initializer-declaration → initializer-head generic-parameter-clause_?_ parameter-clauserethrowsgeneric-where-clause_?_
Объявление подстроки протокола
Протоколы объявляют, что соответствующие типы должны реализовать подстроку, включив объявление подстроки протокола в тело объявления протокола. Объявления подстрок протоколов имеют специальную форму объявления подстроки:
subscript (<#parameters#>) -> <#return type#> { get set } Объявления подстрок объявляют только минимальные требования к реализации геттера и сеттера для типов, которые соответствуют протоколу. Если объявление подстроки включает ключевые слова get и set, соответствующий тип должен реализовать как клаузу геттера, так и клаузу сеттера. Если объявление подстроки включает только ключевое слово get, соответствующий тип должен реализовать по крайней мере клаузу геттера и, по желанию, может реализовать клаузу сеттера.
Чтобы объявить требование статической подстроки в объявлении протокола, отметьте объявление подстроки модификатором объявления static. Структуры и перечисления, которые соответствуют протоколу, объявляют подстроку с ключевым словом static, а классы, которые соответствуют протоколу, объявляют подстроку с ключевым словом static или class. Расширения, добавляющие соответствие протоколу к структуре, перечислению или классу, используют то же ключевое слово, что и использует тип, к которому они относятся. Расширения, которые предоставляют реализацию по умолчанию для требования статической подстроки, используют ключевое слово static.
См. также <doc:Declarations#Subscript-Declaration>.
Грамматика объявления подстроки протокола:
protocol-subscript-declaration → subscript-head subscript-result generic-where-clause_?_ getter-setter-keyword-block
Объявление ассоциированного типа протокола
Протоколы объявляют ассоциированные типы с помощью ключевого слова associatedtype. Ассоциированный тип предоставляет псевдоним для типа, используемого в рамках объявления протокола. Ассоциированные типы похожи на параметры типа в клаузах параметров дженериков, но они связаны с Self в протоколе, в котором они объявлены. В этом контексте Self относится к конечному типу, соответствующему протоколу. Дополнительные сведения и примеры см. в <doc:Generics#Associated-Types>.
Вы используете клаузу дженериков where в объявлении протокола, чтобы добавить ограничения к ассоциированным типам, унаследованным от другого протокола, без повторного объявления ассоциированных типов. Например, объявления SubProtocol ниже эквивалентны:
protocol SomeProtocol {
associatedtype SomeType
}
protocol SubProtocolA: SomeProtocol {
// This syntax produces a warning.
associatedtype SomeType: Equatable
}
// This syntax is preferred.
protocol SubProtocolB: SomeProtocol where SomeType: Equatable { } См. также <doc:Declarations#Type-Alias-Declaration>.
Грамматика объявления ассоциированного типа протокола:
protocol-associated-type-declaration → attributes_?_ access-level-modifier_?_
associatedtypetypealias-name type-inheritance-clause_?_ typealias-assignment_?_ generic-where-clause_?_
Объявление инициализатора
Объявление инициализатора вводит инициализатор для класса, структуры или перечисления в вашу программу. Объявления инициализаторов объявляются с помощью ключевого слова init и имеют две основные формы.
Типы структур, перечислений и классов могут иметь любое количество инициализаторов, но правила и связанное поведение для инициализаторов классов отличаются. В отличие от структур и перечислений, классы имеют два типа инициализаторов: назначенные инициализаторы и инициализаторы по умолчанию, как описано в Инициализация.
Следующая форма объявляет инициализаторы для структур, перечислений и назначенных инициализаторов классов:
init(<#parameters#>) {
<#statements#>
} Определённый инициализатор класса инициализирует все свойства класса напрямую. Он не может вызывать другие инициализаторы того же класса, и если класс имеет суперкласс, он должен вызвать один из определённых инициализаторов суперкласса. Если класс наследует какие-либо свойства от своего суперкласса, один из определённых инициализаторов суперкласса должен быть вызван до того, как любое из этих свойств можно будет установить или изменить в текущем классе.
Определённые инициализаторы могут быть объявлены только в контексте объявления класса и поэтому не могут быть добавлены в класс с помощью объявления расширения.
Инициализаторы в структурах и перечислениях могут вызывать другие объявленные инициализаторы, чтобы делегировать часть или весь процесс инициализации.
Чтобы объявить удобные инициализаторы для класса, отметьте объявление инициализатора модификатором объявления convenience.
convenience init(<#parameters#>) {
<#statements#>
} Удобные инициализаторы могут делегировать процесс инициализации другому удобному инициализатору или одному из определённых инициализаторов класса. При этом процессы инициализации должны заканчиваться вызовом определённого инициализатора, который в конечном итоге инициализирует свойства класса. Удобные инициализаторы не могут вызывать инициализаторы суперкласса.
Вы можете отметить определённые и удобные инициализаторы модификатором объявления required, чтобы потребовать от каждого подкласса реализовать инициализатор. Реализация этого инициализатора в подклассе также должна быть отмечена модификатором объявления required.
По умолчанию инициализаторы, объявленные в суперклассе, не наследуются подклассами. Однако, если подкласс инициализирует все свои хранимые свойства значениями по умолчанию и не определяет никаких собственных инициализаторов, он наследует все инициализаторы суперкласса. Если подкласс переопределяет все определённые инициализаторы суперкласса, он наследует удобные инициализаторы суперкласса.
Как и методы, свойства и индексы, переопределённые определённые инициализаторы нужно отмечать модификатором объявления override.
Примечание: Если вы отмечаете инициализатор модификатором объявления
required, вы также не отмечаете инициализатор модификаторомoverrideпри переопределении требуемого инициализатора в подклассе.
Как и функции и методы, инициализаторы могут выбрасывать или повторно выбрасывать ошибки. И, как и функции и методы, вы используете ключевое слово throws или rethrows после параметров инициализатора, чтобы указать соответствующее поведение. Аналогичным образом, инициализаторы могут быть асинхронными, и вы используете ключевое слово async, чтобы указать это.
Чтобы увидеть примеры инициализаторов в различных объявлениях типов, см. Инициализация.
Инициализаторы с возможностью ошибки
Инициализатор с возможностью ошибки — это тип инициализатора, который производит экземпляр с типом optional или экземпляр с неявным развёрнутым optional типа, для которого объявлен инициализатор. В результате инициализатор с возможностью ошибки может вернуть nil, чтобы указать, что инициализация завершилась неудачно.
Чтобы объявить инициализатор с возможностью ошибки, который производит экземпляр с типом optional, добавьте вопросительный знак к ключевому слову init в объявлении инициализатора (init?). Чтобы объявить инициализатор с возможностью ошибки, который производит экземпляр с неявным развёрнутым optional, добавьте восклицательный знак вместо этого (init!). Пример ниже демонстрирует инициализатор с возможностью ошибки init?, который производит экземпляр с типом optional структуры.
struct SomeStruct {
let property: String
// produces an optional instance of 'SomeStruct'
init?(input: String) {
if input.isEmpty {
// discard 'self' and return 'nil'
return nil
}
property = input
}
} Вы вызываете инициализатор с возможностью ошибки init? так же, как вызываете неизменяемый инициализатор, за исключением того, что вам необходимо обработать опциональность результата.
if let actualInstance = SomeStruct(input: "Hello") {
// do something with the instance of 'SomeStruct'
} else {
// initialization of 'SomeStruct' failed and the initializer returned 'nil'
} Инициализатор с возможностью ошибки может вернуть nil в любой момент в реализации тела инициализатора.
Инициализатор с возможностью ошибки может делегировать любой тип инициализатора. Неизменяемый инициализатор может делегировать другому неизменному инициализатору или инициализатору с возможностью ошибки init!. Неизменяемый инициализатор может делегировать инициализатору с возможностью ошибки init? с помощью принудительного развёртывания результата инициализатора суперкласса — например, написанием super.init()!.
Неудача инициализации распространяется через делегирование инициализатора. В частности, если инициализатор с возможностью ошибки делегирует инициализатору, который завершается неудачно и возвращает nil, тогда инициализатор, который делегировал, также завершается неудачно и неявно возвращает nil. Если неизменяемый инициализатор делегирует инициализатору с возможностью ошибки init!, который завершается неудачно и возвращает nil, тогда возникает ошибка времени выполнения (как если бы вы использовали оператор ! для развёртывания optional, который имеет значение nil).
Определённый инициализатор с возможностью ошибки может быть переопределён в подклассе любым типом определённого инициализатора. Определённый неизменяемый инициализатор может быть переопределён в подклассе только неизменным определённым инициализатором.
Дополнительную информацию и примеры инициализаторов с возможностью ошибки см. в <doc:Initialization#Failable-Initializers>.
Грамматика объявления инициализатора:
initializer-declaration → initializer-head generic-parameter-clause_?_ parameter-clause
async_?_ throws-clause_?_ generic-where-clause_?_ initializer-body
initializer-declaration → initializer-head generic-parameter-clause_?_ parameter-clauseasync_?_rethrowsgeneric-where-clause_?_ initializer-body
initializer-head → attributes_?_ declaration-modifiers_?_init
initializer-head → attributes_?_ declaration-modifiers_?_init?
initializer-head → attributes_?_ declaration-modifiers_?_init!
initializer-body → code-block
Объявление деинициализатора
Объявление деинициализатора объявляет деинициализатор для типа класса. Деинициализаторы не принимают параметров и имеют следующий вид:
deinit {
<#statements#>
} Деинициализатор вызывается автоматически, когда больше нет ссылок на объект класса, непосредственно перед тем, как объект класса будет удалён. Деинициализатор может быть объявлен только в теле объявления класса — но не в расширении класса — и каждый класс может иметь не более одного.
Подкласс наследует деинициализатор своего суперкласса, который неявно вызывается непосредственно перед удалением объекта подкласса. Объект подкласса не удаляется до тех пор, пока все деинициализаторы в его цепочке наследования не закончат свою работу.
Деинициализаторы не вызываются напрямую.
Пример использования деинициализатора в объявлении класса см. в Деинициализация.
Грамматика объявления деинициализатора:
deinitializer-declaration → attributes_?_
deinitcode-block
Объявление расширения
Объявление расширения позволяет расширить поведение существующих типов. Объявления расширений объявляются с помощью ключевого слова extension и имеют следующий вид:
extension <#type name#> where <#requirements#> {
<#declarations#>
} Тело объявления расширения содержит ноль или более объявлений. Эти объявления могут включать вычисляемые свойства, вычисляемые свойства типа, методы экземпляра, методы типа, инициализаторы, объявления индексов и даже объявления классов, структур и перечислений. Объявления расширений не могут содержать объявления деинициализаторов или протоколов, хранимые свойства, наблюдатели свойств или другие объявления расширений. Объявления в расширении протокола не могут быть отмечены final. Обсуждение и несколько примеров расширений, включающих различные виды объявлений, см. в Расширения.
Если имя типа — это класс, структура или перечисление, расширение расширяет этот тип. Если имя типа — это тип протокола, расширение расширяет все типы, которые соответствуют этому протоколу.
Объявления расширений, которые расширяют обобщённый тип или протокол с связанными типами, могут включать требования. Если экземпляр расширенного типа или типа, соответствующего расширенному протоколу, удовлетворяет требованиям, экземпляр получает поведение, указанное в объявлении.
Объявления расширений могут содержать объявления инициализаторов. Однако, если тип, который вы расширяете, определён в другом модуле, объявление инициализатора должно делегировать инициализатору, уже определённому в этом модуле, чтобы обеспечить правильную инициализацию членов этого типа.
Свойства, методы и инициализаторы существующего типа не могут быть переопределены в расширении этого типа.
Объявления расширений могут добавлять соответствие протоколу к существующему классу, структуре или перечислению, указав принятые протоколы:
extension <#type name#>: <#adopted protocols#> where <#requirements#> {
<#declarations#>
} Объявления расширений не могут добавлять наследование классов к существующему классу, и поэтому вы можете указать только список протоколов после имени типа и двоеточия.
Условное соответствие
Вы можете расширить обобщённый тип, чтобы условно соответствовать протоколу, так что экземпляры типа соответствуют протоколу только тогда, когда выполняются определённые требования. Вы добавляете условное соответствие протоколу, включив требования в объявление расширения.
Переопределённые требования не используются в некоторых обобщённых контекстах
В некоторых обобщённых контекстах типы, которые получают поведение от условного соответствия протоколу, не всегда используют специализированные реализации требований этого протокола. Чтобы проиллюстрировать это поведение, следующий пример определяет два протокола и обобщённый тип, который условно соответствует обоим протоколам.
protocol Loggable {
func log()
}
extension Loggable {
func log() {
print(self)
}
}
protocol TitledLoggable: Loggable {
static var logTitle: String { get }
}
extension TitledLoggable {
func log() {
print("\(Self.logTitle): \(self)")
}
}
struct Pair<T>: CustomStringConvertible {
let first: T
let second: T
var description: String {
return "(\(first), \(second))"
}
}
extension Pair: Loggable where T: Loggable { }
extension Pair: TitledLoggable where T: TitledLoggable {
static var logTitle: String {
return "Pair of '\(T.logTitle)'"
}
}
extension String: TitledLoggable {
static var logTitle: String {
return "String"
}
} Структура Pair соответствует протоколам Loggable и TitledLoggable всякий раз, когда её обобщённый тип соответствует Loggable или TitledLoggable соответственно. В примере ниже oneAndTwo — это экземпляр Pair<String>, который соответствует протоколу TitledLoggable, потому что String соответствует протоколу TitledLoggable. Когда метод log() вызывается для oneAndTwo напрямую, используется специализированная версия, содержащая строку заголовка.
let oneAndTwo = Pair(first: "one", second: "two") oneAndTwo.log() // Prints "Pair of 'String': (one, two)"
Однако, когда oneAndTwo используется в общем контексте или как экземпляр протокола Loggable, специализированная версия не используется. Swift выбирает, какую реализацию log() вызвать, консультируясь только с минимальными требованиями, которым должен соответствовать Pair, для работы с Loggable. По этой причине вместо этого используется реализация по умолчанию, предоставляемая протоколом Loggable.
func doSomething<T: Loggable>(with x: T) {
x.log()
}
doSomething(with: oneAndTwo)
// Prints "(one, two)" Когда log() вызывается на экземпляре, переданном в doSomething(_:), напечатанная строка не содержит настроенного заголовка.
Соответствие протоколу не должно быть избыточным
Конкретный тип может соответствовать определенному протоколу только один раз. Swift отмечает избыточные соответствия протоколами как ошибку. Вы, вероятно, столкнетесь с таким типом ошибки в двух ситуациях. Первая ситуация возникает, когда вы явно соответствуют одному и тому же протоколу несколько раз, но с различными требованиями. Вторая ситуация возникает, когда вы неявно наследуете от одного и того же протокола несколько раз. Эти ситуации рассматриваются в разделах ниже.
Устранение явного дублирования
Несколько расширений для конкретного типа не могут добавить соответствие одному и тому же протоколу, даже если требования расширений взаимоисключающие. Данное ограничение продемонстрировано в примере ниже. Два объявления расширений пытаются добавить условное соответствие протоколу Serializable, одно для массивов с элементами Int, а другое — для массивов с элементами String.
protocol Serializable {
func serialize() -> Any
}
extension Array: Serializable where Element == Int {
func serialize() -> Any {
// implementation
}
}
extension Array: Serializable where Element == String {
func serialize() -> Any {
// implementation
}
}
// Error: redundant conformance of 'Array<Element>' to protocol 'Serializable' Если вам необходимо добавить условное соответствие, основанное на нескольких конкретных типах, создайте новый протокол, которому каждый тип может соответствовать, и используйте этот протокол в качестве требования при объявлении условного соответствия.
protocol SerializableInArray { }
extension Int: SerializableInArray { }
extension String: SerializableInArray { }
extension Array: Serializable where Element: SerializableInArray {
func serialize() -> Any {
// implementation
}
} Устранение неявного дублирования
Когда конкретный тип условно соответствует протоколу, этот тип неявно соответствует всем родительским протоколам с одинаковыми требованиями.
Если вам нужен тип, который условно соответствует двум протоколам, которые наследуются от одного родительского, явно укажите соответствие родительскому протоколу. Это предотвращает неявное соответствие родительскому протоколу дважды с разными требованиями.
В следующем примере явно указывается условное соответствие типа Array протоколу Loggable, чтобы избежать конфликта при объявлении его условного соответствия как протоколу TitledLoggable, так и новому протоколу MarkedLoggable.
protocol MarkedLoggable: Loggable {
func markAndLog()
}
extension MarkedLoggable {
func markAndLog() {
print("----------")
log()
}
}
extension Array: Loggable where Element: Loggable { }
extension Array: TitledLoggable where Element: TitledLoggable {
static var logTitle: String {
return "Array of '\(Element.logTitle)'"
}
}
extension Array: MarkedLoggable where Element: MarkedLoggable { } Без расширения для явного объявления условного соответствия протоколу Loggable, другие расширения Array неявно создадут эти объявления, что приведёт к ошибке:
extension Array: Loggable where Element: TitledLoggable { }
extension Array: Loggable where Element: MarkedLoggable { }
// Error: redundant conformance of 'Array<Element>' to protocol 'Loggable' Грамматика объявления расширения:
extension-declaration → attributes_?_ access-level-modifier_?_
extensiontype-identifier type-inheritance-clause_?_ generic-where-clause_?_ extension-body
extension-body →{extension-members_?_}extension-members → extension-member extension-members_?_
extension-member → declaration | compiler-control-statement
Объявление индексации
Объявление индексации позволяет добавить поддержку индексации для объектов определенного типа и обычно используется для предоставления удобной синтаксической конструкции доступа к элементам коллекции, списка или последовательности. Объявления индексации объявляются с помощью ключевого слова subscript и имеют следующий вид:
subscript (<#parameters#>) -> <#return type#> {
get {
<#statements#>
}
set(<#setter name#>) {
<#statements#>
}
} Объявления индексации могут появляться только в контексте класса, структуры, перечисления, расширения или объявления протокола.
Параметры указывают один или несколько индексов, используемых для доступа к элементам соответствующего типа в выражении индексации (например, i в выражении object[i]). Хотя индексы, используемые для доступа к элементам, могут быть любого типа, каждый параметр должен включать аннотацию типа для указания типа каждого индекса. Возвращаемый тип указывает тип элемента, к которому осуществляется доступ.
Как и вычислительные свойства, объявления индексации поддерживают чтение и запись значения доступных элементов. Для чтения значения используется метод получения, а для записи — метод установки. Оператор установки необязателен, и когда нужен только метод получения, можно опустить оба оператора и просто вернуть запрашиваемое значение непосредственно. Однако, если вы предоставите оператор установки, вы также должны предоставить оператор получения.
Имя оператора установки и заключённые в скобки аргументы являются необязательными. Если вы предоставите имя оператора установки, оно будет использоваться в качестве имени параметра для оператора установки. Если вы не предоставите имя оператора установки, по умолчанию параметр оператора установки будет value. Тип параметра оператора установки такой же, как и возвращаемый тип.
Вы можете перегрузить объявление индексации в типе, в котором оно объявлено, при условии, что параметры или возвращаемый тип отличаются от перегружаемого. Вы также можете переопределить объявление индексации, унаследованное от суперкласса. Когда вы делаете это, вы должны пометить переопределённое объявление индексации модификатором объявления override.
Параметры индексации подчиняются тем же правилам, что и параметры функций, за двумя исключениями. По умолчанию параметры, используемые при индексации, не имеют меток аргументов, в отличие от функций, методов и инициализаторов. Однако вы можете предоставить явные метки аргументов, используя тот же синтаксис, что и для функций, методов и инициализаторов. Кроме того, индексация не может содержать параметров «вход-выход». Параметр индексации может иметь значение по умолчанию, используя синтаксис, описанный в <doc:Declarations#Special-Kinds-of-Parameters>.
Вы также можете объявить индексацию в контексте объявления протокола, как описано в <doc:Declarations#Protocol-Subscript-Declaration>.
Дополнительную информацию об индексации и примеры объявлений индексации см. в разделе Индексация.
Объявления индексации типа
Чтобы объявить индексацию, доступную через тип, а не через экземпляры типа, пометьте объявление индексации модификатором объявления static. Классы могут помечать типы вычислительных свойств модификатором объявления class, чтобы подклассы могли переопределять реализацию суперкласса. В объявлении класса ключевое слово static имеет тот же эффект, что и пометка объявления как модификаторами объявления class и final.
Грамматика объявления индексации:
subscript-declaration → subscript-head subscript-result generic-where-clause_?_ code-block
subscript-declaration → subscript-head subscript-result generic-where-clause_?_ getter-setter-block
subscript-declaration → subscript-head subscript-result generic-where-clause_?_ getter-setter-keyword-block
subscript-head → attributes_?_ declaration-modifiers_?_subscriptgeneric-parameter-clause_?_ parameter-clause
subscript-result →->attributes_?_ type
Объявление макроса
Объявление макроса вводит новый макрос. Оно начинается с ключевого слова macro и имеет следующий вид:
macro <#name#> = <#macro implementation#>
Реализация макроса — это другой макрос, указывающий местоположение кода, выполняющего расширение этого макроса. Код, выполняющий расширение макроса, — это отдельная программа Swift, которая использует модуль SwiftSyntax для взаимодействия с кодом Swift. Вызовите макрос externalMacro(module:type:) из стандартной библиотеки Swift, передав имя типа, содержащего реализацию макроса, и имя модуля, содержащего этот тип.
Макросы могут быть перегружены, следуя той же модели, что и функции. Объявление макроса появляется только на уровне файла.
Обзор макросов в Swift см. в разделе Макросы.
Грамматика объявления макроса:
macro-declaration → macro-head identifier generic-parameter-clause_?_ macro-signature macro-definition_?_ generic-where-clause
macro-head → attributes_?_ declaration-modifiers_?_macro
macro-signature → parameter-clause macro-function-signature-result_?_
macro-function-signature-result →->type
macro-definition →=expression
Объявление оператора
Объявление оператора вводит в программу новый инфиксный, префиксный или постфиксный оператор и объявляется с помощью ключевого слова operator.
Вы можете объявлять операторы трёх видов: инфиксные, префиксные и постфиксные. Фиксированность оператора указывает относительное положение оператора по отношению к его операндам.
Существует три основных формы объявления оператора, по одной для каждого вида фиксированности. Фиксированность оператора указывается путём маркировки объявления оператора модификаторами объявления infix, prefix или postfix перед ключевым словом operator. В каждой форме имя оператора может содержать только символы операторов, определённые в <doc:LexicalStructure#Operators>.
Следующая форма объявляет новый инфиксный оператор:
infix operator <#operator name#>: <#precedence group#>
Инфиксный оператор — это бинарный оператор, который записывается между его двумя операндами, например, знакомый оператор сложения (+) в выражении 1 + 2.
Инфиксные операторы могут необязательно указывать группу приоритетов. Если вы опустите группу приоритетов для оператора, Swift использует группу приоритетов по умолчанию, DefaultPrecedence, которая определяет приоритет, немного выше, чем у TernaryPrecedence. Дополнительную информацию см. в <doc:Declarations#Precedence-Group-Declaration>.
Следующая форма объявляет новый префиксный оператор:
prefix operator <#operator name#>
Префиксный оператор — это унарный оператор, который записывается непосредственно перед своим операндом, например, префиксный логический оператор НЕ (!) в выражении !a.
Объявления префиксных операторов не указывают уровень приоритета. Префиксные операторы являются неассоциативными.
Следующая форма объявляет новый постфиксный оператор:
postfix operator <#operator name#>
Постфиксный оператор — это унарный оператор, который записывается непосредственно после своего операнда, например, постфиксный оператор принудительного разбора (!) в выражении a!.
Как и в случае с префиксными операторами, объявления постфиксных операторов не указывают уровень приоритета. Постфиксные операторы являются неассоциативными.
После объявления нового оператора его реализуют, объявив статический метод с тем же именем, что и оператор. Статический метод является членом одного из типов, значения которых оператор принимает в качестве аргумента — например, оператор, который умножает Double на Int, реализуется как статический метод в структуре Double или Int. Если вы реализуете префиксный или постфиксный оператор, вы должны также пометить объявление этого метода соответствующим модификатором prefix или postfix. Чтобы увидеть пример создания и реализации нового оператора, см. <doc:AdvancedOperators#Custom-Operators>.
Грамматика объявления оператора:
operator-declaration → prefix-operator-declaration | postfix-operator-declaration | infix-operator-declaration
prefix-operator-declaration →
prefixoperatoroperator
postfix-operator-declaration →postfixoperatoroperator
infix-operator-declaration →infixoperatoroperator infix-operator-group_?_infix-operator-group →
:precedence-group-name
Объявление группы приоритета
Объявление группы приоритета вводит новую группировку приоритета инфиксных операторов в вашу программу. Приоритет оператора определяет, насколько сильно оператор связывает свои операнды при отсутствии группирующих скобок.
Объявление группы приоритета имеет следующий формат:
precedencegroup <#precedence group name#> {
higherThan: <#lower group names#>
lowerThan: <#higher group names#>
associativity: <#associativity#>
assignment: <#assignment#>
} Список имен групп ниже и имен групп выше определяет отношение новой группы приоритета к существующим группам приоритета. Атрибут приоритета lowerThan может использоваться только для ссылки на группы приоритета, объявленные за пределами текущего модуля. Когда два оператора конкурируют за свои операнды, например, в выражении 2 + 3 * 5, оператор с более высоким относительным приоритетом связывает свои операнды сильнее.
Примечание: Группы приоритета, связанные друг с другом с помощью имен групп ниже и имен групп выше, должны вписываться в одну реляционную иерархию, но они не должны образовывать линейную иерархию. Это означает, что возможны группы приоритетов с неопределённым относительным приоритетом. Операторы из этих групп приоритета не могут использоваться рядом друг с другом без группирующих скобок.
Swift определяет множество групп приоритетов в соответствии с операторами, предоставляемыми стандартной библиотекой Swift. Например, операторы сложения (+) и вычитания (-) относятся к группе AdditionPrecedence, а операторы умножения (*) и деления (/) — к группе MultiplicationPrecedence. Полный список групп приоритетов, предоставляемых стандартной библиотекой Swift, см. в Объявлениях операторов.
Ассоциативность оператора определяет, как последовательность операторов с одинаковым уровнем приоритета группируется при отсутствии группирующих скобок. Вы указываете ассоциативность оператора, написав одно из контекстно-зависимых ключевых слов left, right или none — если вы опустите ассоциативность, по умолчанию будет none. Операторы, которые являются левоассоциативными, группируются слева направо. Например, оператор вычитания (-) является левоассоциативным, поэтому выражение 4 - 5 - 6 группируется как (4 - 5) - 6 и вычисляется как -7. Операторы, которые являются правоассоциативными, группируются справа налево, а операторы, для которых указана ассоциативность none, вообще не ассоциируются. Неассоциативные операторы одного уровня приоритета не могут появляться рядом друг с другом. Например, оператор < имеет ассоциативность none, что означает, что 1 < 2 < 3 не является допустимым выражением.
Присваивание группы приоритета определяет приоритет оператора при использовании в операции, включающей необязательную цепочку вызовов. Если значение установлено на true, оператор в соответствующей группе приоритета использует те же правила группирования во время необязательной цепочки вызовов, что и операторы присваивания из стандартной библиотеки Swift. В противном случае, если значение установлено на false или опущено, операторы в группе приоритета следуют тем же правилам необязательной цепочки вызовов, что и операторы, которые не выполняют присваивание.
Грамматика объявления группы приоритета:
precedence-group-declaration →
precedencegroupprecedence-group-name{precedence-group-attributes_?_}precedence-group-attributes → precedence-group-attribute precedence-group-attributes_?_
precedence-group-attribute → precedence-group-relation
precedence-group-attribute → precedence-group-assignment
precedence-group-attribute → precedence-group-associativityprecedence-group-relation →
higherThan:precedence-group-names
precedence-group-relation →lowerThan:precedence-group-namesprecedence-group-assignment →
assignment:boolean-literalprecedence-group-associativity →
associativity:left
precedence-group-associativity →associativity:right
precedence-group-associativity →associativity:noneprecedence-group-names → precedence-group-name | precedence-group-name
,precedence-group-names
precedence-group-name → identifier
Модификаторы объявления
Модификаторы объявления — это ключевые слова или контекстно-зависимые ключевые слова, которые изменяют поведение или значение объявления. Вы указываете модификатор объявления, написав соответствующее ключевое слово или контекстно-зависимое ключевое слово между атрибутами объявления (если таковые имеются) и ключевым словом, которое вводит объявление.
термин
class: Примените этот модификатор к члену класса, чтобы указать, что член является членом самого класса, а не членом экземпляров класса. Члены суперкласса, имеющие этот модификатор и не имеющие модификатораfinal, могут быть переопределены подклассами.-
термин
dynamic: Примените этот модификатор к любому члену класса, который может быть представлен Objective-C. Когда вы помечаете объявление члена модификаторомdynamic, доступ к этому члену всегда динамически передаётся с использованием Objective-C runtime. Доступ к этому члену никогда не встраивается и не девиртуализируется компилятором.Поскольку объявления, помеченные модификатором
dynamic, передаются с использованием Objective-C runtime, они должны быть помечены атрибутомobjc. термин
final: Примените этот модификатор к классу или к свойству, методу или индексному члену класса. Он применяется к классу, чтобы указать, что класс не может быть подклассом. Он применяется к свойству, методу или индексному члену класса, чтобы указать, что член класса не может быть переопределён в любом подклассе. Пример использования атрибутаfinalсм. в <doc:Inheritance#Preventing-Overrides>.термин
lazy: Примените этот модификатор к хранимому свойству переменной класса или структуры, чтобы указать, что начальное значение свойства вычисляется и хранится не более одного раза, когда свойство впервые обращается к нему. Пример использования модификатораlazyсм. в <doc:Properties#Lazy-Stored-Properties>.-
термин
optional: Примените этот модификатор к членам свойства, метода или индекса протокола, чтобы указать, что соответствующий тип не обязан реализовывать эти члены.Вы можете применять модификатор
optionalтолько к протоколам, помеченным атрибутомobjc. В результате, только типы классов могут принимать и соответствовать протоколу, который содержит необязательные требования к членам. Дополнительную информацию об использовании модификатораoptionalи рекомендации по доступу к необязательным членам протокола — например, когда вы не уверены, реализует ли их соответствующий тип — см. в <doc:Protocols#Optional-Protocol-Requirements>.
термин
required: Примените этот модификатор к обозначенному или удобному инициализатору класса, чтобы указать, что каждый подкласс должен реализовать этот инициализатор. Реализация этого инициализатора в подклассе также должна быть помечена модификаторомrequired.термин
static: Примените этот модификатор к элементу структуры, класса, перечисления или протокола, чтобы указать, что элемент является членом типа, а не членом экземпляров этого типа. В области объявления класса применение модификатораstaticк объявлению члена эквивалентно применению модификаторовclassиfinalк этому объявлению члена. Однако свойства типа константы класса являются исключением:staticимеет свое обычное, неклассовое значение, так как вы не можете применитьclassилиfinalк этим объявлениям.термин
unowned: Примените этот модификатор к хранимой переменной, константе или хранимому свойству, чтобы указать, что переменная или свойство имеют невладеющую ссылку на объект, хранящийся в качестве ее значения. Если вы попытаетесь получить доступ к переменной или свойству после того, как объект был удален из памяти, будет поднято исключение во время выполнения. Как и в слабой ссылке, тип свойства или значения должен быть типом класса; в отличие от слабой ссылки, тип не является необязательным. Пример и дополнительная информация о модификатореunownedсм. в <doc:AutomaticReferenceCounting#Unowned-References>.термин
unowned(safe): Явное написаниеunowned.термин
unowned(unsafe): Примените этот модификатор к хранимой переменной, константе или хранимому свойству, чтобы указать, что переменная или свойство имеют невладеющую ссылку на объект, хранящийся в качестве ее значения. Если вы попытаетесь получить доступ к переменной или свойству после того, как объект был удален из памяти, вы получите доступ к памяти по адресу, где раньше находился объект, что является небезопасной операцией с точки зрения памяти. Как и в слабой ссылке, тип свойства или значения должен быть типом класса; в отличие от слабой ссылки, тип не является необязательным. Пример и дополнительная информация о модификатореunownedсм. в <doc:AutomaticReferenceCounting#Unowned-References>.термин
weak: Примените этот модификатор к хранимой переменной или хранимому свойству переменной, чтобы указать, что переменная или свойство имеют слабую ссылку на объект, хранящийся в качестве ее значения. Тип переменной или свойства должен быть необязательным типом класса. Если вы получаете доступ к переменной или свойству после того, как объект был удален из памяти, ее значение являетсяnil. Пример и дополнительная информация о модификатореweakсм. в <doc:AutomaticReferenceCounting#Weak-References>.
Уровни управления доступом
Swift предоставляет пять уровней управления доступом: open, public, internal, file private и private. Вы можете пометить объявление одним из модификаторов уровня доступа ниже, чтобы указать уровень доступа к объявлению. Подробно о контроле доступа см. в AccessControl.
термин
open: Примените этот модификатор к объявлению, чтобы указать, что к объявлению можно получить доступ и его можно наследоваться кодом в том же модуле, что и объявление. Объявления, помеченные модификатором уровня доступаopen, также могут быть доступны и наследоваться кодом в модуле, который импортирует модуль, содержащий это объявление.термин
public: Примените этот модификатор к объявлению, чтобы указать, что к объявлению можно получить доступ и его можно наследоваться кодом в том же модуле, что и объявление. Объявления, помеченные модификатором уровня доступаpublic, также могут быть доступны (но не наследоваться) кодом в модуле, который импортирует модуль, содержащий это объявление.термин
package: Примените этот модификатор к объявлению, чтобы указать, что к объявлению можно получить доступ только кодом в том же пакете, что и объявление. Пакет — это единица распространения кода, которую вы определяете в используемой системе сборки. Когда система сборки компилирует код, она указывает имя пакета, передавая флаг-package-nameкомпилятору Swift. Два модуля являются частью одного пакета, если система сборки указывает одно и то же имя пакета при их сборке.термин
internal: Примените этот модификатор к объявлению, чтобы указать, что к объявлению можно получить доступ только кодом в том же модуле, что и объявление. По умолчанию большинство объявлений неявно помечены модификатором уровня доступаinternal.термин
fileprivate: Примените этот модификатор к объявлению, чтобы указать, что к объявлению можно получить доступ только кодом в том же файле, что и объявление.термин
private: Примените этот модификатор к объявлению, чтобы указать, что к объявлению можно получить доступ только кодом внутри непосредственного окружающего раздела объявления.
В целях контроля доступа расширения ведут себя следующим образом:
Если в одном файле несколько расширений, и все они расширяют один и тот же тип, то все эти расширения имеют одинаковый охват управления доступом. Расширения и тип, который они расширяют, могут находиться в разных файлах.
Если расширения находятся в том же файле, что и тип, который они расширяют, то расширения имеют такой же охват управления доступом, как и тип, который они расширяют.
К закрытым членам, объявленным в объявлении типа, можно получить доступ из расширений этого типа. К закрытым членам, объявленным в одном расширении, можно получить доступ из других расширений и из объявления расширяемого типа.
Каждый модификатор уровня доступа выше необязательно принимает один аргумент, состоящий из ключевого слова set в скобках — например, private(set). Используйте эту форму модификатора уровня доступа, когда хотите указать уровень доступа к сеттеру переменной или индексатора, который меньше или равен уровню доступа к самой переменной или индексатору, как описано в <doc:AccessControl#Getters-and-Setters>.
Грамматика модификатора объявления:
declaration-modifier →
class|convenience|dynamic|final|infix|lazy|optional|override|postfix|prefix|required|static|unowned|unowned(safe)|unowned(unsafe)|weak
declaration-modifier → access-level-modifier
declaration-modifier → mutation-modifier
declaration-modifier → actor-isolation-modifier
declaration-modifiers → declaration-modifier declaration-modifiers_?_access-level-modifier →
private|private(set)
access-level-modifier →fileprivate|fileprivate(set)
access-level-modifier →internal|internal(set)
access-level-modifier →package|package(set)
access-level-modifier →public|public(set)
access-level-modifier →open|open(set)mutation-modifier →
mutating|nonmutatingactor-isolation-modifier →
nonisolated
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