Spec-Zone.ru › Swift Language

Выражения

Доступ, изменение и присваивание значений.

В Swift существует четыре вида выражений: префиксные выражения, инфиксные выражения, первичные выражения и постфиксные выражения. Оценка выражения возвращает значение, вызывает побочный эффект или оба.

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

Грамматика выражения:

выражение → оператор_try_?_ оператор_await_?_ префиксное_выражение инфиксные_выражения_?_

Префиксные выражения

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

Сведения о поведении этих операторов см. в разделах BasicOperators и AdvancedOperators.

Сведения об операторах, предоставляемых стандартной библиотекой Swift, см. в разделе Operator Declarations.

Грамматика префиксного выражения:

префиксное_выражение → префиксный_оператор_?_ постфиксное_выражение
префиксное_выражение → выражение_с_in_out

Выражение с in-out

Выражение с in-out отмечает переменную, которая передается в качестве аргумента in-out в выражении вызова функции.

&<#expression#>

Дополнительную информацию об параметрах in-out и пример см. в <doc:Functions#In-Out-Parameters>.

Выражения с in-out также используются при предоставлении аргумента без указания на адрес в контексте, где нужен указатель, как описано в <doc:Expressions#Implicit-Conversion-to-a-Pointer-Type>.

Грамматика выражения с in-out:

выражение_с_in_out → & первичное_выражение

Оператор try

Выражение try состоит из оператора try, за которым следует выражение, которое может выбросить ошибку. Оно имеет следующий вид:

try <#expression#>

Значение выражения try равно значению выражения expression.

Выражение optional-try состоит из оператора try?, за которым следует выражение, которое может выбросить ошибку. Оно имеет следующий вид:

try? <#expression#>

Если выражение expression не выбросит ошибку, значение выражения optional-try — это необязательное значение, содержащее значение выражения expression. В противном случае значение выражения optional-try равно nil.

Выражение forced-try состоит из оператора try!, за которым следует выражение, которое может выбросить ошибку. Оно имеет следующий вид:

try! <#expression#>

Значение выражения forced-try равно значению выражения expression. Если выражение expression выбросит ошибку, произойдет ошибка выполнения.

Когда выражение слева от инфиксного оператора помечено операторами try, try? или try!, этот оператор применяется ко всему инфиксной выражению. Тем не менее, вы можете использовать скобки, чтобы явно указать область применения оператора.

// try applies to both function calls
sum = try someThrowingFunction() + anotherThrowingFunction()

// try applies to both function calls
sum = try (someThrowingFunction() + anotherThrowingFunction())

// Error: try applies only to the first function call
sum = (try someThrowingFunction()) + anotherThrowingFunction()

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

Если выражение содержит как оператор try, так и оператор await, оператор try должен стоять первым.

Дополнительную информацию и примеры использования try, try? и try! см. в разделе ErrorHandling.

Грамматика выражения try:

оператор_try → try | try ? | try !

Оператор await

Выражение await состоит из оператора await, за которым следует выражение, использующее результат асинхронной операции. Оно имеет следующий вид:

await <#expression#>

Значение выражения await равно значению выражения expression.

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

Выражение await может появиться только в асинхронном контексте, таком как замыкание, передаваемое функции async(priority:operation:). Оно не может появиться в теле оператора defer или в автозамыкании синхронного типа функции.

Когда выражение слева от инфиксного оператора помечено оператором await, этот оператор применяется ко всему инфиксному выражению. Тем не менее, вы можете использовать скобки, чтобы явно указать область применения оператора.

// await applies to both function calls
sum = await someAsyncFunction() + anotherAsyncFunction()

// await applies to both function calls
sum = await (someAsyncFunction() + anotherAsyncFunction())

// Error: await applies only to the first function call
sum = (await someAsyncFunction()) + anotherAsyncFunction()

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

Если выражение содержит как оператор await, так и оператор try, оператор try должен стоять первым.

Грамматика выражения await:

оператор_await → await

Инфиксные выражения

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

<#left-hand argument#> <#operator#> <#right-hand argument#>

Сведения о поведении этих операторов см. в разделах BasicOperators и AdvancedOperators.

Сведения об операторах, предоставляемых стандартной библиотекой Swift, см. в разделе Operator Declarations.

Примечание: Во время разбора выражение, состоящее из инфиксных операторов, представляется в виде плоского списка. Этот список преобразуется в дерево с применением приоритета операторов. Например, выражение 2 + 3 * 5 изначально понимается как плоский список из пяти элементов, 2, +, 3, * и 5. Этот процесс преобразует его в дерево (2 + (3 * 5)).

Грамматика инфиксного выражения:

инфиксное_выражение → инфиксный_оператор префиксное_выражение
инфиксное_выражение → оператор_присваивания оператор_try_?_ оператор_await_?_ префиксное_выражение
инфиксное_выражение → условный_оператор оператор_try_?_ оператор_await_?_ префиксное_выражение
инфиксное_выражение → оператор_приведения_типов
инфиксные_выражения → инфиксное_выражение инфиксные_выражения_?_

Оператор присваивания

Оператор присваивания задаёт новое значение для данного выражения. Он имеет следующий вид:

<#expression#> = <#value#>

Значение expression устанавливается в значение, полученное путём оценки value. Если expression является кортежем, value должно быть кортежем с тем же количеством элементов. (Допускаются вложенные кортежи.) Присваивание выполняется от каждой части value к соответствующей части expression. Например:

(a, _, (b, c)) = ("test", 9.45, (12, 3))
// a is "test", b is 12, c is 3, and 9.45 is ignored

Оператор присваивания не возвращает никакого значения.

Грамматика оператора присваивания:

оператор_присваивания → =

Условный оператор

Условный оператор возвращает одно из двух заданных значений в зависимости от значения условия. Он имеет следующий вид:

<#condition#> ? <#expression used if true#> : <#expression used if false#>

Если condition оценивается как true, условный оператор оценивает первое выражение и возвращает его значение. В противном случае он оценивает второе выражение и возвращает его значение. Неиспользуемое выражение не оценивается.

Пример использования условного оператора см. в <doc:BasicOperators#Ternary-Conditional-Operator>.

Грамматика условного оператора:

условный_оператор → ? выражение :

Операторы приведения типов

Существует четыре оператора приведения типов: оператор is, оператор as, оператор as? и оператор as!.

Они имеют следующий вид:

<#expression#> is <#type#>
<#expression#> as <#type#>
<#expression#> as? <#type#>
<#expression#> as! <#type#>

Оператор is проверяет во время выполнения, можно ли привести выражение к указанному типу. Он возвращает true, если выражение можно привести к указанному типу; в противном случае он возвращает false.

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

func f(_ any: Any) { print("Function for Any") }
func f(_ int: Int) { print("Function for Int") }
let x = 10
f(x)
// Prints "Function for Int"

let y: Any = x
f(y)
// Prints "Function for Any"

f(x as Any)
// Prints "Function for Any"

Приведение через мост позволяет использовать выражение типа стандартной библиотеки Swift, например, String, как соответствующий тип Foundation, например, NSString, без необходимости создания нового экземпляра. Дополнительную информацию о приведении через мост см. в Working with Foundation Types.

Оператор as? выполняет условное приведение выражения к указанному типу. Оператор as? возвращает необязательный тип указанного типа. Во время выполнения, если приведение успешно, значение выражения оборачивается в необязательный тип и возвращается; в противном случае возвращаемое значение равно nil. Если приведение к указанному типу гарантированно завершится неудачей или гарантированно завершится успехом, возникает ошибка времени компиляции.

Оператор as! выполняет принудительное приведение выражения к указанному типу. Оператор as! возвращает значение указанного типа, а не необязательный тип. Если приведение завершается неудачей, возникает ошибка времени выполнения. Поведение оператора x as! T такое же, как поведение оператора (x as? T)!.

Дополнительную информацию о приведении типов и примеры использования операторов приведения типов см. в разделе TypeCasting.

Грамматика оператора приведения типов:

type-casting-operator → is type
type-casting-operator → as type
type-casting-operator → as ? type
type-casting-operator → as ! type

Основные выражения

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

Грамматика основного выражения:

primary-expression → identifier generic-argument-clause_?_
primary-expression → literal-expression
primary-expression → self-expression
primary-expression → superclass-expression
primary-expression → conditional-expression
primary-expression → closure-expression
primary-expression → parenthesized-expression
primary-expression → tuple-expression
primary-expression → implicit-member-expression
primary-expression → wildcard-expression
primary-expression → macro-expansion-expression
primary-expression → key-path-expression
primary-expression → selector-expression
primary-expression → key-path-string-expression

Литеральное выражение

Литеральное выражение состоит либо из обычной литеральной константы (например, строки или числа), либо из литерала массива или словаря, либо из литерала игрового поля.

Примечание: До Swift 5.9 следующие специальные литералы распознавались: #column, #dsohandle, #fileID, #filePath, #file, #function и #line. Сейчас они реализованы как макросы в стандартной библиотеке Swift: column(), dsohandle(), fileID(), filePath(), file(), function(), и line().

Литерал массива — это упорядоченная коллекция значений. Он имеет следующий вид:

[<#value 1#>, <#value 2#>, <#...#>]

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

var emptyArray: [Double] = []

Литерал словаря — это неупорядоченная коллекция пар ключ-значение. Он имеет следующий вид:

[<#key 1#>: <#value 1#>, <#key 2#>: <#value 2#>, <#...#>]

За последним выражением в словаре может следовать необязательная запятая. Значение литерала словаря имеет тип [Key: Value], где Key — это тип выражений ключей, а Value — это тип выражений значений. Если есть выражения нескольких типов, Key и Value являются ближайшими общими супертипами для их соответствующих значений. Пустой литерал словаря записывается как двоеточие внутри пары скобок ([:]), чтобы отличить его от пустого литерала массива. Вы можете использовать пустой литерал словаря для создания пустого словаря заданных типов ключей и значений.

var emptyDictionary: [String: Double] = [:]

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

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

Грамматика литерального выражения:

literal-expression → literal
literal-expression → array-literal | dictionary-literal | playground-literal

array-literal → [ array-literal-items_?_ ]
array-literal-items → array-literal-item ,_?_ | array-literal-item , array-literal-items
array-literal-item → expression

dictionary-literal → [ dictionary-literal-items ] | [ : ]
dictionary-literal-items → dictionary-literal-item ,_?_ | dictionary-literal-item , dictionary-literal-items
dictionary-literal-item → expression : expression

playground-literal → #colorLiteral ( red : expression , green : expression , blue : expression , alpha : expression )
playground-literal → #fileLiteral ( resourceName : expression )
playground-literal → #imageLiteral ( resourceName : expression )

Выражение self

Выражение self — это явное обращение к текущему типу или экземпляру типа, в котором оно встречается. Оно имеет следующие формы:

self
self.<#member name#>
self[<#subscript index#>]
self(<#initializer arguments#>)
self.init(<#initializer arguments#>)

В инициализаторе, подскрипте или методе экземпляра self относится к текущему экземпляру типа, в котором он встречается. В методе типа self относится к текущему типу, в котором он встречается.

Выражение self используется для указания области видимости при доступе к членам, обеспечивая разграничение, когда в области видимости существует другая переменная с тем же именем, например, параметр функции. Например:

class SomeClass {
    var greeting: String
    init(greeting: String) {
        self.greeting = greeting
    }
}

В изменяющем методе типа значения вы можете присвоить новый экземпляр этого типа значения self. Например:

struct Point {
    var x = 0.0, y = 0.0
    mutating func moveBy(x deltaX: Double, y deltaY: Double) {
        self = Point(x: x + deltaX, y: y + deltaY)
    }
}

Грамматика выражения self:

self-expression → self | self-method-expression | self-subscript-expression | self-initializer-expression

self-method-expression → self . identifier
self-subscript-expression → self [ function-call-argument-list ]
self-initializer-expression → self . init

Выражение суперкласса

Выражение суперкласса позволяет классу взаимодействовать со своим суперклассом. Оно имеет одну из следующих форм:

super.<#member name#>
super[<#subscript index#>]
super.init(<#initializer arguments#>)

Первая форма используется для доступа к члену суперкласса. Вторая форма используется для доступа к реализации подскрипта суперкласса. Третья форма используется для доступа к инициализатору суперкласса.

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

Грамматика выражения суперкласса:

superclass-expression → superclass-method-expression | superclass-subscript-expression | superclass-initializer-expression

superclass-method-expression → super . identifier
superclass-subscript-expression → super [ function-call-argument-list ]
superclass-initializer-expression → super . init

Выражение условного оператора

Выражение условного оператора вычисляет одно из нескольких заданных значений на основе значения условия. Оно имеет один из следующих видов:

if <#condition 1#> {
   <#expression used if condition 1 is true#>
} else if <#condition 2#> {
   <#expression used if condition 2 is true#>
} else {
   <#expression used if both conditions are false#>
}

switch <#expression#> {
case <#pattern 1#>:
    <#expression 1#>
case <#pattern 2#> where <#condition#>:
    <#expression 2#>
default:
    <#expression 3#>
}

Выражение условного оператора имеет такое же поведение и синтаксис, как оператор if или оператор switch, за исключением различий, которые описаны в последующих абзацах.

Выражение условного оператора используется только в следующих контекстах:

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

Ветви выражения условного оператора являются исчерпывающими, гарантируя, что выражение всегда производит значение независимо от условия. Это означает, что каждая ветвь if требует соответствующей ветви else.

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

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

let number: Double = if someCondition { 10 } else { 12.34 }
let number = if someCondition { 10 as Double } else { 12.34 }

Внутри генератора результатов выражения условного оператора могут появляться только в качестве начального значения переменной или константы. Это поведение означает, что когда вы пишете if или switch в генераторе результатов — за пределами объявления переменной или константы — этот код понимается как оператор ветвления, и один из методов генератора результатов преобразует этот код.

Не помещайте выражение условного оператора в выражение try, даже если одна из ветвей выражения условного оператора генерирует ошибку.

Грамматика выражения условного оператора:

conditional-expression → if-expression | switch-expression

if-expression → if condition-list { statement } if-expression-tail
if-expression-tail → else if-expression
if-expression-tail → else { statement }

switch-expression → switch expression { switch-expression-cases }
switch-expression-cases → switch-expression-case switch-expression-cases_?_
switch-expression-case → case-label statement
switch-expression-case → default-label statement

Выражение замыкания

Выражение замыкания создаёт замыкание, также известное как лямбда-функция или анонимная функция в других языках программирования. Как и объявление функции, замыкание содержит операторы и захватывает константы и переменные из окружающего контекста. Оно имеет следующий вид:

{ (<#parameters#>) -> <#return type#> in
   <#statements#>
}

Параметры имеют тот же вид, что и параметры в объявлении функции, как описано в <doc:Declarations#Function-Declaration>.

Написание throws или async в выражении замыкания явно отмечает замыкание как выбрасывающее или асинхронное.

{ (<#parameters#>) async throws -> <#return type#> in
   <#statements#>
}

Если тело замыкания включает оператор throws или выражение try, которое не вложено в оператор do с исчерпывающей обработкой ошибок, замыкание считается выбрасывающим. Если выбрасывающее замыкание выбрасывает ошибки только одного типа, замыкание считается выбрасывающим этот тип ошибки; в противном случае оно считается выбрасывающим any Error. Аналогично, если тело включает выражение await, оно считается асинхронным.

Существуют несколько специальных форм, которые позволяют записывать замыкания более лаконично:

  • Замыкание может опустить типы своих параметров, тип возвращаемого значения или оба. Если вы опускаете имена параметров и оба типа, опустите ключевое слово in перед операторами. Если опущенные типы нельзя вывести, возникает ошибка времени компиляции.
  • Замыкание может опустить имена своих параметров. Тогда его параметры неявно называются $, за которыми следуют их позиции: $0, $1, $2 и так далее.
  • Замыкание, состоящее только из одного выражения, считается возвращающим значение этого выражения. Содержимое этого выражения также учитывается при выполнении вывода типов в окружающем выражении.

Следующие выражения замыканий эквивалентны:

myFunction { (x: Int, y: Int) -> Int in
    return x + y
}

myFunction { x, y in
    return x + y
}

myFunction { return $0 + $1 }

myFunction { $0 + $1 }

Сведения о передаче замыкания в качестве аргумента функции см. в <doc:Expressions#Function-Call-Expression>.

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

Дополнительную информацию об escaping замыканиях см. в <doc:Closures#Escaping-Closures>.

Списки захватов

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

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

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

var a = 0
var b = 0
let closure = { [a] in
 print(a, b)
}

a = 10
b = 10
closure()
// Prints "0 10"

Есть две разные вещи, называемые a, переменная в окружающем контексте и константа в области замыкания, но только одна переменная, называемая b. a во внутреннем контексте инициализируется значением a во внешнем контексте при создании замыкания, но их значения не связаны каким-либо особым образом. Это означает, что изменение значения a во внешнем контексте не влияет на значение a во внутреннем контексте, и изменение a внутри замыкания не влияет на значение a вне замыкания. В противоположность этому, существует только одна переменная, называемая b — b во внешнем контексте — поэтому изменения изнутри или снаружи замыкания видны в обоих местах.

Это различие не видно, когда тип захваченной переменной имеет семантику ссылок. Например, в приведённом ниже коде есть две вещи, называемые x, переменная во внешнем контексте и константа во внутреннем контексте, но они обе ссылаются на один и тот же объект из-за семантики ссылок.

class SimpleClass {
    var value: Int = 0
}
var x = SimpleClass()
var y = SimpleClass()
let closure = { [x] in
    print(x.value, y.value)
}

x.value = 10
y.value = 10
closure()
// Prints "10 10"

Если тип значения выражения является классом, вы можете пометить выражение в списке захватов с помощью weak или unowned, чтобы захватить слабую или неуправляемую ссылку на значение выражения.

myFunction { print(self.title) }                    // implicit strong capture
myFunction { [self] in print(self.title) }          // explicit strong capture
myFunction { [weak self] in print(self!.title) }    // weak capture
myFunction { [unowned self] in print(self.title) }  // unowned capture

Вы также можете связать произвольное выражение с именованным значением в списке захватов. Выражение вычисляется при создании замыкания, и значение захватывается со указанной силой. Например:

// Weak capture of "self.parent" as "parent"
myFunction { [weak parent = self.parent] in print(parent!.title) }

Дополнительную информацию и примеры выражений замыкания см. в <doc:Closures#Closure-Expressions>. Дополнительную информацию и примеры списков захватов см. в <doc:AutomaticReferenceCounting#Resolving-Strong-Reference-Cycles-for-Closures>.

Грамматика выражения замыкания:

closure-expression → { attributes_?_ closure-signature_?_ statements_?_ }

closure-signature → capture-list_?_ closure-parameter-clause async_?_ throws-clause_?_ function-result_?_ in
closure-signature → capture-list in

closure-parameter-clause → ( ) | ( closure-parameter-list ) | identifier-list
closure-parameter-list → closure-parameter | closure-parameter , closure-parameter-list
closure-parameter → closure-parameter-name type-annotation_?_
closure-parameter → closure-parameter-name type-annotation ...
closure-parameter-name → identifier

capture-list → [ capture-list-items ]
capture-list-items → capture-list-item | capture-list-item , capture-list-items
capture-list-item → capture-specifier_?_ identifier
capture-list-item → capture-specifier_?_ identifier = expression
capture-list-item → capture-specifier_?_ self-expression
capture-specifier → weak | unowned | unowned(safe) | unowned(unsafe)

Неявное выражение члена

Неявное выражение члена — это сокращённый способ доступа к члену типа, например, случаю перечисления или методу типа, в контексте, где вывод типа может определить подразумеваемый тип. Оно имеет следующий вид:

.<#member name#>

Например:

var x = MyEnumeration.someValue
x = .anotherValue

Если выведенный тип — это необязательный, вы также можете использовать член ненулевого типа в неявном выражении члена.

var someOptional: MyEnumeration? = .someValue

Неявные выражения членов могут следовать за постфиксным оператором или другой постфиксной синтаксической конструкцией, перечисленной в <doc:Expressions#Postfix-Expressions>. Это называется цепным неявным выражением члена. Хотя обычно все цепные постфиксные выражения имеют одинаковый тип, единственное требование заключается в том, что всё цепное неявное выражение члена должно быть приводимо к типу, подразумеваемому его контекстом. В частности, если подразумеваемый тип — это необязательный, вы можете использовать значение ненулевого типа, а если подразумеваемый тип — тип класса, вы можете использовать значение одного из его подклассов. Например:

class SomeClass {
    static var shared = SomeClass()
    static var sharedSubclass = SomeSubclass()
    var a = AnotherClass()
}
class SomeSubclass: SomeClass { }
class AnotherClass {
    static var s = SomeClass()
    func f() -> SomeClass { return AnotherClass.s }
}
let x: SomeClass = .shared.a.f()
let y: SomeClass? = .shared
let z: SomeClass = .sharedSubclass

В приведенном коде тип x точно соответствует типу, подразумеваемому его контекстом, тип y приводим из типа SomeClass к типу SomeClass?, а тип z приводим из типа SomeSubclass к типу SomeClass.

Грамматика неявного выражения члена:

implicit-member-expression → . identifier
implicit-member-expression → . identifier . postfix-expression

Выражение в скобках

Выражение в скобках состоит из выражения, окружённого круглыми скобками. Вы можете использовать скобки для указания приоритета операций, явно группируя выражения. Группирующие скобки не изменяют тип выражения — например, тип (1) просто равен типу Int.

Грамматика выражения в скобках:

parenthesized-expression → ( expression )

Кортежное выражение

Кортежное выражение состоит из списка выражений, разделённых запятыми, окружённого круглыми скобками. Каждое выражение может иметь необязательный идентификатор перед ним, разделённый двоеточием (:). Оно имеет следующий вид:

(<#identifier 1#>: <#expression 1#>, <#identifier 2#>: <#expression 2#>, <#...#>)

Каждый идентификатор в кортежном выражении должен быть уникальным в области видимости кортежного выражения. Вложенном кортежном выражении идентификаторы на одном уровне вложенности должны быть уникальными. Например, (a: 10, a: 20) недействительно, потому что метка a встречается дважды на одном уровне. Однако (a: 10, b: (a: 1, x: 2)) действительно — хотя a встречается дважды, это происходит один раз во внешнем кортеже и один раз во внутреннем.

Кортежное выражение может содержать ноль выражений или два или более выражений. Одно выражение внутри скобок — это выражение в скобках.

Примечание: как пустое кортежное выражение, так и пустой кортежный тип записываются как () в Swift. Поскольку Void — это псевдоним типа (), вы можете использовать его для записи пустого кортежного типа. Однако, как и все псевдонимы типов, Void всегда является типом — вы не можете использовать его для записи пустого кортежного выражения.

Грамматика кортежного выражения:

tuple-expression → ( ) | ( tuple-element , tuple-element-list )
tuple-element-list → tuple-element | tuple-element , tuple-element-list
tuple-element → expression | identifier : expression

Выражение с подстановкой

Выражение с подстановкой используется для явного игнорирования значения во время присваивания. Например, в следующем присваивании 10 присваивается x, а 20 игнорируется:

(x, _) = (10, 20)
// x is 10, and 20 is ignored

Грамматика выражения с подстановкой:

wildcard-expression → _

Выражение макрорасширения

Выражение макрорасширения состоит из имени макроса, за которым следует список аргументов макроса, разделённых запятыми, в круглых скобках. Макрос расширяется на этапе компиляции. Выражения макрорасширения имеют следующий вид:

<#macro name#>(<#macro argument 1#>, <#macro argument 2#>)

Выражение макрорасширения опускает скобки после имени макроса, если макрос не принимает аргументов.

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

func f(a: Int = #line, b: Int = (#line), c: Int = 100 + #line) {
    print(a, b, c)
}
f()  // Prints "4 1 101"

В приведенной выше функции значение по умолчанию для a — это единственное выражение макроса, поэтому этот макрос оценивается в месте вызова f(a:b:c:). В отличие от этого, значения для b и c — это выражения, содержащие макрос — макросы в этих выражениях оцениваются в месте определения f(a:b:c:).

При использовании макроса в качестве значения по умолчанию он проверяется на соответствие типу без расширения макроса для проверки следующих требований:

  • Уровень доступа макроса такой же или менее ограниченный, чем у функции, использующей его.
  • Макрос либо не принимает аргументов, либо его аргументы — это литералы без интерполяции строк.
  • Возвращаемый тип макроса соответствует типу параметра.

Вы используете выражения макроса для вызова самостоятельных макросов. Чтобы вызвать прикреплённый макрос, используйте синтаксис пользовательских атрибутов, описанный в Атрибуты. Как самостоятельные, так и прикреплённые макросы расширяются следующим образом:

  1. Swift анализирует исходный код, чтобы получить абстрактное синтаксическое дерево (AST).

  2. Реализация макроса получает узлы AST в качестве входных данных и выполняет необходимые преобразования.

  3. Преобразованные узлы AST, созданные реализацией макроса, добавляются в исходное AST.

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

Для вложенных макросов и прикреплённых макросов, имеющих несколько ролей, процесс расширения повторяется. Вложенные выражения макрорасширения расширяются снаружи вовнутрь. Например, в приведённом ниже коде outerMacro(_:) расширяется в первую очередь, а нерасширенный вызов innerMacro(_:) появляется в абстрактном синтаксическом дереве, которое outerMacro(_:) получает в качестве входных данных.

#outerMacro(12, #innerMacro(34), "some text")

Прикреплённый макрос, имеющий несколько ролей, расширяется один раз для каждой роли. Каждое расширение получает тот же исходный AST в качестве входных данных. Swift формирует общее расширение, собирая все созданные узлы AST и помещая их в соответствующие места в AST.

Обзор макросов в Swift см. в Макросы.

Грамматика выражения макрорасширения:

macro-expansion-expression → # identifier generic-argument-clause_?_ function-call-argument-clause_?_ trailing-closures_?_

Выражение ключевого пути

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

\<#type name#>.<#path#>

Имя типа — это имя конкретного типа, включая любые обобщённые параметры, такие как String, [Int] или Set<Int>.

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

При компиляции выражение с ключевым путем заменяется экземпляром класса KeyPath.

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

struct SomeStructure {
    var someValue: Int
}

let s = SomeStructure(someValue: 12)
let pathToProperty = \SomeStructure.someValue

let value = s[keyPath: pathToProperty]
// value is 12

Имя типа можно опустить в контекстах, где вывод типа может определить подразумеваемый тип. Следующий код использует \.someProperty вместо \SomeClass.someProperty:

class SomeClass: NSObject {
    @objc dynamic var someProperty: Int
    init(someProperty: Int) {
        self.someProperty = someProperty
    }
}

let c = SomeClass(someProperty: 10)
c.observe(\.someProperty) { object, change in
    // ...
}

Путь может ссылаться на self для создания ключевого пути идентичности (\.self). Ключевой путь идентичности ссылается на весь экземпляр, поэтому вы можете использовать его для доступа к данным, хранящимся в переменной, и изменения всех этих данных в одном шаге. Например:

var compoundValue = (a: 1, b: 2)
// Equivalent to compoundValue = (a: 10, b: 20)
compoundValue[keyPath: \.self] = (a: 10, b: 20)

Путь может содержать несколько имён свойств, разделённых точками, чтобы сослаться на свойство значения свойства. Этот код использует выражение ключевого пути \OuterStructure.outer.someValue для доступа к свойству someValue свойства OuterStructure типа outer:

struct OuterStructure {
    var outer: SomeStructure
    init(someValue: Int) {
        self.outer = SomeStructure(someValue: someValue)
    }
}

let nested = OuterStructure(someValue: 24)
let nestedKeyPath = \OuterStructure.outer.someValue

let nestedValue = nested[keyPath: nestedKeyPath]
// nestedValue is 24

Путь может включать индексы с помощью квадратных скобок, при условии, что тип параметра индекса соответствует протоколу Hashable. В этом примере используется индекс в ключевом пути для доступа ко второму элементу массива:

let greetings = ["hello", "hola", "bonjour", "안녕"]
let myGreeting = greetings[keyPath: \[String].[1]]
// myGreeting is 'hola'

Значение, используемое в индексе, может быть именованным значением или литералом. Значения захватываются в ключевых путях с помощью семантики значений. Следующий код использует переменную index как в выражении ключевого пути, так и в замыкании для доступа к третьему элементу массива greetings. Когда index изменяется, выражение ключевого пути всё ещё ссылается на третий элемент, в то время как замыкание использует новый индекс.

var index = 2
let path = \[String].[index]
let fn: ([String]) -> String = { strings in strings[index] }

print(greetings[keyPath: path])
// Prints "bonjour"
print(fn(greetings))
// Prints "bonjour"

// Setting 'index' to a new value doesn't affect 'path'
index += 1
print(greetings[keyPath: path])
// Prints "bonjour"

// Because 'fn' closes over 'index', it uses the new value
print(fn(greetings))
// Prints "안녕"

Путь может использовать необязательную цепочку и принудительное распаковку. Этот код использует необязательную цепочку в ключевом пути для доступа к свойству необязательной строки:

let firstGreeting: String? = greetings.first
print(firstGreeting?.count as Any)
// Prints "Optional(5)"

// Do the same thing using a key path.
let count = greetings[keyPath: \[String].first?.count]
print(count as Any)
// Prints "Optional(5)"

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

let interestingNumbers = ["prime": [2, 3, 5, 7, 11, 13, 17],
                          "triangular": [1, 3, 6, 10, 15, 21, 28],
                          "hexagonal": [1, 6, 15, 28, 45, 66, 91]]
print(interestingNumbers[keyPath: \[String: [Int]].["prime"]] as Any)
// Prints "Optional([2, 3, 5, 7, 11, 13, 17])"
print(interestingNumbers[keyPath: \[String: [Int]].["prime"]![0]])
// Prints "2"
print(interestingNumbers[keyPath: \[String: [Int]].["hexagonal"]!.count])
// Prints "7"
print(interestingNumbers[keyPath: \[String: [Int]].["hexagonal"]!.count.bitWidth])
// Prints "64"

Вы можете использовать выражение ключевого пути в контекстах, где обычно используется функция или замыкание. В частности, вы можете использовать выражение ключевого пути, корневой тип которого является SomeType, а путь которого генерирует значение типа Value вместо функции или замыкания типа (SomeType) -> Value.

struct Task {
    var description: String
    var completed: Bool
}
var toDoList = [
    Task(description: "Practice ping-pong.", completed: false),
    Task(description: "Buy a pirate costume.", completed: true),
    Task(description: "Visit Boston in the Fall.", completed: false),
]

// Both approaches below are equivalent.
let descriptions = toDoList.filter(\.completed).map(\.description)
let descriptions2 = toDoList.filter { $0.completed }.map { $0.description }

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

func makeIndex() -> Int {
    print("Made an index")
    return 0
}
// The line below calls makeIndex().
let taskKeyPath = \[Task][makeIndex()]
// Prints "Made an index"

// Using taskKeyPath doesn't call makeIndex() again.
let someTask = toDoList[keyPath: taskKeyPath]

Для получения дополнительной информации об использовании ключевых путей в коде, взаимодействующем с API Objective-C, см. Использование функций среды выполнения Objective-C в Swift. Для получения информации о ключевом кодировании значений и наблюдении за значениями см. Руководство по программированию ключевого кодирования значений и Руководство по программированию наблюдения за значениями.

Грамматика выражения ключевого пути:

выражение_ключевого_пути → \ тип_?_ . компоненты_ключевого_пути
компоненты_ключевого_пути → компонент_ключевого_пути | компонент_ключевого_пути . компоненты_ключевого_пути
компонент_ключевого_пути → идентификатор постфиксы_ключевого_пути_?_ | постфиксы_ключевого_пути

постфиксы_ключевого_пути → постфикс_ключевого_пути постфиксы_ключевого_пути_?_
постфикс_ключевого_пути → ? | ! | self | [ список_аргументов_вызова_функции ]

Выражение селектора

Выражение селектора позволяет получить доступ к селектору, используемому для ссылки на метод или геттер/сеттер свойства в Objective-C. Оно имеет следующий вид:

#selector(<#method name#>)
#selector(getter: <#property name#>)
#selector(setter: <#property name#>)

Имя метода и имя свойства должны ссылаться на метод или свойство, доступные в среде выполнения Objective-C. Значение выражения селектора — это экземпляр типа Selector. Например:

class SomeClass: NSObject {
    @objc let property: String

    @objc(doSomethingWithInt:)
    func doSomething(_ x: Int) { }

    init(property: String) {
        self.property = property
    }
}
let selectorForMethod = #selector(SomeClass.doSomething(_:))
let selectorForPropertyGetter = #selector(getter: SomeClass.property)

При создании селектора для геттера свойства, имя свойства может ссылаться на переменное или константное свойство. В противоположность этому, при создании селектора для сеттера свойства, имя свойства должно ссылаться только на переменное свойство.

Имя метода может содержать скобки для группировки, а также оператор as для разграничения методов с одинаковым именем, но разными сигнатурами типов. Например:

extension SomeClass {
    @objc(doSomethingWithString:)
    func doSomething(_ x: String) { }
}
let anotherSelector = #selector(SomeClass.doSomething(_:) as (SomeClass) -> (String) -> Void)

Так как селектор создаётся во время компиляции, а не во время выполнения, компилятор может проверить, существует ли метод или свойство и доступен ли он в среде выполнения Objective-C.

Примечание: Хотя имя метода и имя свойства являются выражениями, они никогда не вычисляются.

Для получения дополнительной информации об использовании селекторов в коде Swift, взаимодействующем с API Objective-C, см. Использование функций среды выполнения Objective-C в Swift.

Грамматика выражения селектора:

выражение_селектора → #selector ( выражение )
выражение_селектора → #selector ( getter: выражение )
выражение_селектора → #selector ( setter: выражение )

Выражение строки ключевого пути

Выражение строки ключевого пути позволяет получить доступ к строке, используемой для ссылки на свойство в Objective-C, для использования в API ключевого кодирования значений и наблюдения за значениями. Оно имеет следующий вид:

#keyPath(<#property name#>)

Имя свойства должно ссылаться на свойство, доступное в среде выполнения Objective-C. Во время компиляции выражение строки ключевого пути заменяется строковым литералом. Например:

class SomeClass: NSObject {
    @objc var someProperty: Int
    init(someProperty: Int) {
       self.someProperty = someProperty
    }
}

let c = SomeClass(someProperty: 12)
let keyPath = #keyPath(SomeClass.someProperty)

if let value = c.value(forKey: keyPath) {
    print(value)
}
// Prints "12"

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

extension SomeClass {
    func getSomeKeyPath() -> String {
        return #keyPath(someProperty)
    }
}
print(keyPath == c.getSomeKeyPath())
// Prints "true"

Поскольку строка ключевого пути создается во время компиляции, а не во время выполнения, компилятор может проверить, существует ли свойство и доступно ли оно в среде выполнения Objective-C.

Для получения дополнительной информации об использовании ключевых путей в коде Swift, взаимодействующем с API Objective-C, см. Использование функций среды выполнения Objective-C в Swift. Для получения информации о ключевом кодировании значений и наблюдении за значениями см. Руководство по программированию ключевого кодирования значений и Руководство по программированию наблюдения за значениями.

Примечание: Хотя имя свойства является выражением, оно никогда не вычисляется.

Грамматика выражения строки ключевого пути:

выражение_строки_ключевого_пути → #keyPath ( выражение )

Постфиксные выражения

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

Для получения информации о поведении этих операторов, см. BasicOperators и AdvancedOperators.

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

Грамматика постфиксного выражения:

постфиксное_выражение → начальное_выражение
постфиксное_выражение → постфиксное_выражение постфиксный_оператор
постфиксное_выражение → выражение_вызова_функции
постфиксное_выражение → выражение_инициализации
постфиксное_выражение → явное_выражение_члена
постфиксное_выражение → постфиксное_выражение_self
постфиксное_выражение → выражение_подстроки
постфиксное_выражение → выражение_принудительного_значения
постфиксное_выражение → выражение_необязательной_цепочки

Выражение вызова функции

Выражение вызова функции состоит из имени функции, за которым следует список аргументов функции, разделённых запятыми, в скобках. Выражения вызова функции имеют следующий вид:

<#function name#>(<#argument value 1#>, <#argument value 2#>)

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

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

<#function name#>(<#argument name 1#>: <#argument value 1#>, <#argument name 2#>: <#argument value 2#>)

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

// someFunction takes an integer and a closure as its arguments
someFunction(x: x, f: { $0 == 13 })
someFunction(x: x) { $0 == 13 }

// anotherFunction takes an integer and two closures as its arguments
anotherFunction(x: x, f: { $0 == 13 }, g: { print(99) })
anotherFunction(x: x) { $0 == 13 } g: { print(99) }

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

// someMethod takes a closure as its only argument
myData.someMethod() { $0 == 13 }
myData.someMethod { $0 == 13 }

Для включения заключительных замыканий в аргументы компилятор проверяет параметры функции слева направо следующим образом:

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

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

Параметр структурно напоминает тип функции, если параметр не является параметром ввода/вывода и он является одним из следующих:

  • Параметр, тип которого является типом функции, например (Bool) -> Int
  • Параметр автозамыкания, тип обернутого выражения которого является типом функции, например @autoclosure () -> ((Bool) -> Int)
  • Параметр с переменным числом параметров, тип элемента массива которого является типом функции, например ((Bool) -> Int)...
  • Параметр, тип которого обернут в один или несколько слоев необязательных типов, например Optional<(Bool) -> Int>
  • Параметр, тип которого объединяет эти разрешенные типы, например (Optional<(Bool) -> Int>)...

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

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

typealias Callback = (Int) -> Int
func someFunction(firstClosure: Callback? = nil,
                secondClosure: Callback? = nil) {
    let first = firstClosure?(10)
    let second = secondClosure?(20)
    print(first ?? "-", second ?? "-")
}

someFunction()  // Prints "- -"
someFunction { return $0 + 100 }  // Ambiguous
someFunction { return $0 } secondClosure: { return $0 }  // Prints "10 20"

В примере выше функция вызова, помеченная как «Неоднозначная», выводит «- 120» и генерирует предупреждение компилятора в Swift 5.3. В будущей версии Swift будет выведено «110 -».

Тип класса, структуры или перечисления может включить синтаксический сахар для синтаксиса вызова функции, объявив один из нескольких методов, как описано в <doc:Declarations#Methods-with-Special-Names>.

Неявное преобразование к типу указателя

В выражении вызова функции, если аргумент и параметр имеют разные типы, компилятор пытается привести их типы к соответствию, применяя одно из неявных преобразований в следующем списке:

  • inout SomeType может стать UnsafePointer<SomeType> или UnsafeMutablePointer<SomeType>
  • inout Array<SomeType> может стать UnsafePointer<SomeType> или UnsafeMutablePointer<SomeType>
  • Array<SomeType> может стать UnsafePointer<SomeType>
  • String может стать UnsafePointer<CChar>

Следующие два вызова функции эквивалентны:

func unsafeFunction(pointer: UnsafePointer<Int>) {
    // ...
}
var myNumber = 1234

unsafeFunction(pointer: &myNumber)
withUnsafePointer(to: myNumber) { unsafeFunction(pointer: $0) }

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

Примечание: при неявном преобразовании массива в небезопасный указатель Swift гарантирует, что хранилище массива является непрерывным, преобразуя или копируя массив по мере необходимости. Например, вы можете использовать этот синтаксис с массивом, который был связан с Array из подкласса NSArray, который не имеет API-контракта относительно своего хранилища. Если вам нужно гарантировать, что хранилище массива уже непрерывно, поэтому неявное преобразование никогда не должно выполнять эту работу, используйте ContiguousArray вместо Array.

Использование & вместо явной функции, такой как withUnsafePointer(to:), может помочь сделать вызовы функций низкого уровня C более читабельными, особенно когда функция принимает несколько аргументов-указателей. Однако при вызове функций из другого Swift-кода избегайте использования & вместо использования небезопасных API напрямую.

Грамматика выражения вызова функции:

выражение-вызова-функции → выражение-постфиксное оператор-вызова-функции
выражение-вызова-функции → выражение-постфиксное оператор-вызова-функции_?_ заключительные-замыкания

оператор-вызова-функции → ( ) | ( список-аргументов-вызова-функции )
список-аргументов-вызова-функции → аргумент-вызова-функции | аргумент-вызова-функции , список-аргументов-вызова-функции
аргумент-вызова-функции → выражение | идентификатор : выражение
аргумент-вызова-функции → оператор | идентификатор : оператор

заключительные-замыкания → выражение-замыкания меченые-заключительные-замыкания_?_
меченые-заключительные-замыкания → меченые-заключительные-замыкания меченые-заключительные-замыкания_?_
меченые-заключительное-замыкание → идентификатор : выражение-замыкания

Выражение инициализации

Выражение инициализации предоставляет доступ к инициализатору типа. Оно имеет следующий вид:

<#expression#>.init(<#initializer arguments#>)

Вы используете выражение инициализации в выражении вызова функции для инициализации нового экземпляра типа. Вы также используете выражение инициализации для делегирования инициализатору суперкласса.

class SomeSubClass: SomeSuperClass {
    override init() {
        // subclass initialization goes here
        super.init()
    }
}

Как и функция, инициализатор может использоваться как значение. Например:

// Type annotation is required because String has multiple initializers.
let initializer: (Int) -> String = String.init
let oneTwoThree = [1, 2, 3].map(initializer).reduce("", +)
print(oneTwoThree)
// Prints "123"

Если вы указываете тип по имени, вы можете получить доступ к инициализатору типа без использования выражения инициализации. Во всех остальных случаях вы должны использовать выражение инициализации.

let s1 = SomeType.init(data: 3)  // Valid
let s2 = SomeType(data: 1)       // Also valid

let s3 = type(of: someValue).init(data: 7)  // Valid
let s4 = type(of: someValue)(data: 5)       // Error

Грамматика выражения инициализации:

выражение-инициализации → выражение-постфиксное . init
выражение-инициализации → выражение-постфиксное . init ( имена-аргументов )

Явное выражение члена

Явное выражение члена позволяет получить доступ к членам именованного типа, кортежа или модуля. Оно состоит из точки (.) между элементом и идентификатором его члена.

<#expression#>.<#member name#>

Члены именованного типа называются в рамках объявления или расширения типа. Например:

class SomeClass {
    var someProperty = 42
}
let c = SomeClass()
let y = c.someProperty  // Member access

Члены кортежа неявно называются с помощью целых чисел в порядке их появления, начиная с нуля. Например:

var t = (10, 20, 30)
t.0 = t.1
// Now t is (20, 20, 30)

Члены модуля получают доступ к объявлениям верхнего уровня этого модуля.

Типы, объявленные с атрибутом dynamicMemberLookup, включают члены, которые ищутся во время выполнения, как описано в Атрибуты.

Чтобы отличить методы или инициализаторы, имена которых различаются только именами их аргументов, укажите имена аргументов в скобках, при этом каждое имя аргумента следует за двоеточием (:). Запишите знак подчеркивания (_) для аргумента без имени. Чтобы отличить перегруженные методы, используйте аннотацию типа. Например:

class SomeClass {
    func someMethod(x: Int, y: Int) {}
    func someMethod(x: Int, z: Int) {}
    func overloadedMethod(x: Int, y: Int) {}
    func overloadedMethod(x: Int, y: Bool) {}
}
let instance = SomeClass()

let a = instance.someMethod              // Ambiguous
let b = instance.someMethod(x:y:)        // Unambiguous

let d = instance.overloadedMethod        // Ambiguous
let d = instance.overloadedMethod(x:y:)  // Still ambiguous
let d: (Int, Bool) -> Void  = instance.overloadedMethod(x:y:)  // Unambiguous

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

let x = [10, 3, 20, 15, 4]
    .sorted()
    .filter { $0 > 5 }
    .map { $0 * 100 }

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

let numbers = [10, 20, 33, 43, 50]
#if os(iOS)
    .filter { $0 < 40 }
#else
    .filter { $0 > 25 }
#endif

Между #if, #endif и другими директивами компиляции блок условной компиляции может содержать неявное выражение члена, за которым следуют ноль или более постфиксов, чтобы сформировать постфиксное выражение. Он также может содержать другой блок условной компиляции или сочетание этих выражений и блоков.

Вы можете использовать этот синтаксис везде, где вы можете написать явное выражение члена, а не только в коде верхнего уровня.

В блоке условной компиляции ветвь для директивы компиляции #if должна содержать по крайней мере одно выражение. Другие ветви могут быть пустыми.

Грамматика явного выражения члена:

явное-выражение-члена → выражение-постфиксное . десятичные-цифры
явное-выражение-члена → выражение-постфиксное . идентификатор оператор-обобщения_?_
явное-выражение-члена → выражение-постфиксное . идентификатор ( имена-аргументов )
явное-выражение-члена → выражение-постфиксное блок-условной-компиляции

имена-аргументов → имя-аргумента имена-аргументов_?_
имя-аргумента → идентификатор :

Постфиксное выражение self

Постфиксное выражение self состоит из выражения или имени типа, сразу за которым следует .self. Оно имеет следующие формы:

<#expression#>.self
<#type#>.self

Первая форма оценивается как значение выражения. Например, x.self оценивается как x.

Вторая форма вычисляет значение type. Используйте эту форму для доступа к типу как к значению. Например, так как SomeClass.self вычисляет SomeClass тип сам по себе, вы можете передать его в функцию или метод, принимающий аргумент уровня типа.

Грамматика постфиксного выражения с «self»:

postfix-self-expression → postfix-expression . self

Выражение с индексом

Выражение с индексом предоставляет доступ к индексу с помощью геттера и сеттера соответствующего объявления индекса. Оно имеет следующий вид:

<#expression#>[<#index expressions#>]

Для вычисления значения выражения с индексом вызывается геттер индекса для типа выражения expression с передачей выражений index expressions в качестве параметров индекса. Для установки значения вызывается сеттер индекса аналогичным образом.

Дополнительная информация об объявлениях индексов находится в <doc:Declarations#Protocol-Subscript-Declaration>.

Грамматика выражения с индексом:

subscript-expression → postfix-expression [ function-call-argument-list ]

Выражение принудительного значения

Выражение принудительного значения раскрывает значение опционального значения, в котором вы уверены, что оно не nil. Оно имеет следующий вид:

<#expression#>!

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

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

var x: Int? = 0
x! += 1
// x is now 1

var someDictionary = ["a": [1, 2, 3], "b": [10, 20]]
someDictionary["a"]![0] = 100
// someDictionary is now ["a": [100, 2, 3], "b": [10, 20]]

Грамматика выражения принудительного значения:

forced-value-expression → postfix-expression !

Выражение цепочки опциональных значений

Выражение цепочки опциональных значений предоставляет упрощенную синтаксическую конструкцию для использования опциональных значений в постфиксных выражениях. Оно имеет следующий вид:

<#expression#>?

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

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

Если постфиксное выражение, содержащее выражение цепочки опциональных значений, вложено в другие постфиксные выражения, только внешнее выражение возвращает опциональный тип. В примере ниже, когда c не nil, его значение раскрывается и используется для вычисления .property, значение которого используется для вычисления .performAction(). Всё выражение c?.property.performAction() имеет значение опционального типа.

var c: SomeClass?
var result: Bool? = c?.property.performAction()

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

var result: Bool?
if let unwrappedC = c {
    result = unwrappedC.property.performAction()
}

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

func someFunctionWithSideEffects() -> Int {
    return 42  // No actual side effects.
}
var someDictionary = ["a": [1, 2, 3], "b": [10, 20]]

someDictionary["not here"]?[0] = someFunctionWithSideEffects()
// someFunctionWithSideEffects isn't evaluated
// someDictionary is still ["a": [1, 2, 3], "b": [10, 20]]

someDictionary["a"]?[0] = someFunctionWithSideEffects()
// someFunctionWithSideEffects is evaluated and returns 42
// someDictionary is now ["a": [42, 2, 3], "b": [10, 20]]

Грамматика выражения цепочки опциональных значений:

optional-chaining-expression → postfix-expression ?

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

Spec-Zone.ru

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