Документация

Выражения

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

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

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

expression → try-operator ­opt­ prefix-expression ­binary-expressions­opt­
expression-list → expression­  | expression­expression-list­

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

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

Подробнее об этих операторах см. Базовые операторы и Продвинутые операторы.

Получить информацию об операторах, предоставляемых стандартной библиотекой Swift, см. Operator Declarations.

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

Грамматика префиксных выражений

prefix-expression prefix-operator­opt ­postfix-expression­
prefix-expression in-out-expression­
in-out-expression &­ identifier­

Оператор Try

Выражение Try состоит из оператора try, за которым следует выражение, которое может генерировать ошибку. Выглядит вот так:

try выражение

Опциальное выражение try состоит из оператора try?, за которым следует выражение, которое может генерировать ошибку. Выглядит вот так:

try? выражение

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

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

try! выражение

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

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

sum = try someThrowingFunction() + anotherThrowingFunction()   // try применяется к обоим вызовам функций
sum = try (someThrowingFunction() + anotherThrowingFunction()) // try применяется к обоим вызовам функций
sum = (try someThrowingFunction()) + anotherThrowingFunction() // Error: try применяется только к первой функции

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

Подробнее о try, try?, и try! см. Обработка ошибок.

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

try-operator try­ | try­?­ | try­!­

Бинарные выражения

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

аргумент слева оператор аргумент справа

Подробнее см. Базовые операторы и Продвинутые операторы.

Подробнее о операторах, представленных в стандартной библиотеке Swift читайте в Operator Declarations.

Заметка

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

Грамматика бинарных выражений

binary-expression → binary-operator­ prefix-expression­
binary-expression → assignment-operator ­try-operator­opt ­prefix-expression­
binary-expression → conditional-operator­ try-operator­opt ­prefix-expression­
binary-expression → type-casting-operator­
binary-expressions → binary-expression ­binary-expressions­opt­

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

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

выражение = значение

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

(a, _, (b, c)) = ("test", 9.45, (12, 3))
// a равен "test", b равно 12, c равно 3, а 9.45 игнорируется

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

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

assignment-operator 

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

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

условие ? выражение при true : выражение при false

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

Подробнее см. Тернарный условный оператор.

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

conditional-operator → expression­

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

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

Выглядят они вот так:

выражение is тип
выражение as тип
выражение as? тип
выражение as! тип

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

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

func f(_ any: Any) { print("Функция для Any") }
func f(_ int: Int) { print("Функция для Int") }
let x = 10
f(x)
// Выведет "Функция для Int"
 
let y: Any = x
f(y)
// Выведет "Функция для Any"
 
f(x as Any)
// Выведет "Функция для Any”

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

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

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

Подробнее см. Приведение типов.

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

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­ opt­
primary-expression → literal-expression­
primary-expression → self-expression­
primary-expression → superclass-expression­
primary-expression → closure-expression­
primary-expression → parenthesized-expression­
primary-expression → tuple-expression­
primary-expression → implicit-member-expression­
primary-expression → wildcard-expression­
primary-expression → key-path-expression­
primary-expression → selector-expression­
primary-expression → key-path-string-expression

Выражения литерала

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

Литерал Тип Значение
#file String Имя файла, в котором он появляется.
#line Int Номер строки, в которой он появляется.
#column Int Номер столбца, с которого он начинается.
#function String Имя объявления, в котором он появляется.
#dsohandle UnsafeRawPointer DSO - динамический общий объект. Дескриптор DSO, который используется при появлении данного маркера.

Внутри функции значение #function - это имя этой функции, внутри метода - это имя этого метода, внутри свойства геттера или сеттера - это имя этого свойства, внутри специальных элементов, таких как init или subscript - это имя ключевого слова, а на верхнем уровне файла - это имя текущего модуля.

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

func logFunctionName(string: String = #function) {
    print(string)
}
func myFunction() {
   logFunctionName() // Выведет "myFunction()".
}

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

[значение 1, значение 2, ...]

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

var emptyArray: [Double] = []

Литерал словаря представляет собой неупорядоченный набор пар ключ-значение. Выглядит он так:

[ключ 1: значение 1, ключ 2: значение 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­
literal-expression → #file­  | #line­ | #column­  |  #function­  |  #dsohandle array-literal → array-literal-items­opt­ array-literal-items → array-literal-item­opt­  array-literal-item­array-literal-items­ array-literal-item → expression­ dictionary-literal → dictionary-literal-items­ | dictionary-literal-items → dictionary-literal-item­opt­ 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(initialiser arguments)
self.init(initialiser arguments)

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

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

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

В mutating методе типа значения, вы можете назначить новый экземпляр этого типа значения на 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­expression-list­ self-initializer-expression → self­init­

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

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

super.имя члена
super[индекс сабскрипта]
super.init(аргументы инициализатора)

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

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

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

superclass-expression → superclass-method-expression­  | superclass-subscript-expression­ | superclass-initializer-expression­
superclass-method-expression → super­identifier­ superclass-subscript-expression → super­expression-list­ superclass-initializer-expression → super­init­

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

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

{ (parameters) -> return type in
statements
}

Параметры имеют ту же форму, что и параметры в объявлении функции, как описано в Функции.

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

  • Замыкание может опускать типы его параметров, тип возвращаемого значения, или все вместе. Если опустить имена параметров и оба типа, то необходимо опустить ключевое слово 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 }

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

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

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

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

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

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

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

var a = 0
var b = 0

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

a = 10
b = 10

closure()
// Выведет "0 10"

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

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

class SimpleClass {
    var value = 0
}

var x = SimpleClass()
var y = SimpleClass()

let closure = { [x] in
    print(x.value, y.value)
}

x.value = 10
y.value = 10

closure()
// Выведет "10 10"

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

myFunction { print(self.title) }                    // strong захват
myFunction { [weak self] in print(self!.title) }    // weak захват
myFunction { [unowned self] in print(self.title) }  // unowned захват

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

// Weak захват "self.parent" как "parent"
myFunction { [weak parent = self.parent] in print(parent!.title) }

Подробнее о выражениях замыкания см. Замыкания.  Подробнее о списках захвата см. Замена циклов сильных ссылок в замыканиях.

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

closure-expression → {closure-signature opt­ statements}
‌closure-signature → capture-list opt­ closure-parameter-clause throws  opt­ function-result opt­ 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-nametype-annotation opt­
‌closure-parameter → closure-parameter-nametype-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 opt­ expression
‌capture-specifier → weak  | unowned | unowned(safe) |  unowned(unsafe)

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

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

.имя члена

Например:

var x = MyEnumeration.SomeValue
x = .AnotherValue

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

implicit-member-expression identifier­

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

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

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

parenthesized-expression → expression­)

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

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

(identifier 1: expression 1, identifier 2: expression 2, ...)

Выражение кортежа может иметь 0 выражений или может содержать два или более выражений. Если в скобках находится всего одно выражение, то это уже является выражением в скобках, а не кортежем.

Заметка

В 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­

Выражение wildcard

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

(x, _) = (10, 20)
// x равно 10, а значение 20 игнорируется

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

wildcard-expression 

Выражение Key-Path

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

\type name.path

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

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

Во время компиляции выражение key-path подменяется экземпляром класса KeyPath.

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


struct SomeStructure {
var someValue: Int
}

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

let value = s[keyPath: pathToProperty]
// value равно 12

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

class SomeClass: NSObject {
@objc 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)

Path (или путь) может содержать в себе несколько имен свойств, разделенных между собой точками, ссылаясь на свойство значения свойства. Этот код использует выражения key-path \OuterStructure.outer.someValue, для того, чтобы получить доступ к свойству someValue свойства outer структуры OuterStructure:

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 равно 24

Path может включать в себя сабскрипты, использующие квадратные скобочки, если тип параметра соответсвует протоколу Hashable. Этот пример использует сабскрипты в key-path для того, чтобы получить доступ ко второму элементу массива:

let greetings = ["hello", "hola", "bonjour", "안녕"]
let myGreeting = greetings[keyPath: \[String].[1]]
// myGreeting равно 'hola'

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

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

print(greetings[keyPath: path])
// Выведет "bonjour"
print(fn(greetings))
// Выведет "bonjour"

// Присваивание 'index' нового значения не имеет никакого эффекта на 'path'
index += 1
print(greetings[keyPath: path])
// Выведет "bonjour"

// Так как 'fn' закрывает 'index', то оно использует новое его новое значение
print(fn(greetings))
// Выведет "안녕"

Path может использовать опциональную последовательность и принудительно извлеченные опционалы. Этот код использует опциональную последовательность в выражении key-path, чтобы получить значение свойства имеющего тип опциональной строки:

let firstGreeting: String? = greetings.first
print(firstGreeting?.count as Any)
// Выведет "Optional(5)"

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

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

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

Для более подробной информации по использованию key-path в коде, который взаимодействует с Objective-C APIs, смотрите Using Objective-C Runtime Features in Swift. Для более подробной информацией по KVC и KVO смотрите Key-Value Coding Programming Guide и Key-Value Observing Programming Guide.

Грамматика выражения Key-Path

key-path-expression → \ type opt . key-path-components
key-path-components → key-path-component | key-path-component . key-path-components
key-path-component → identifier key-path-postfixes opt | key-path-postfixes
key-path-postfixes → key-path-postfix key-path-postfixes opt
key-path-postfix → ? | ! | [ function-call-argument-list ] 

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

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

#selector(имя метода)
#selector(getter: имя свойства)
#selector(setter: имя свойства)

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

class SomeClass: NSObject {
    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-expression → #selector ( expression )
‌selector-expression → #selector ( getter: expression )
‌selector-expression → #selector ( setter: expression )

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

Выражения пути по ключу позволяют вам получить доступ к строке, которая относится к свойству в Objective-C для использования в KVC и в KVO API's.

#keyPath(имя свойства)

Имя свойства должно ссылаться на свойство, которое доступно во время исполнения в 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)
}
// Выведет "12"

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

extension SomeClass {
    func getSomeKeyPath() -> String {
        return #keyPath(someProperty)
    }
}
print(keyPath == c.getSomeKeyPath())
// Выведет"true"

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

Заметка

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

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

key-path-expression → #keyPath ( expression )

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

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

Для получения информации о поведении этих операторов см. Базовые операторы и Продвинутые операторы.

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

Грамматика постфиксных выражений

postfix-expression → primary-expression­
postfix-expression → postfix-expression­postfix-operator­
postfix-expression → function-call-expression­
postfix-expression → initializer-expression­
postfix-expression → explicit-member-expression­
postfix-expression → postfix-self-expression­
postfix-expression → subscript-expression­
postfix-expression → forced-value-expression­
postfix-expression → optional-chaining-expression­

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

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

имя функции(значение аргумента №1, значение аргумента №2)

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

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

имя функции(имя аргумента №1: значение аргумента №1,
имя аргумента №2: значение аргумента №2)

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

// someFunction принимает Int и замыкание в качестве своих аргументов
someFunction(x: x, f: {$0 == 13})
someFunction(x: x) {$0 == 13}

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

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

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

function-call-expression → postfix-expression function-call-argument-clause
function-call-expression → postfix-expression ­parenthesized-expression­opt­ trailing-closure

function-call-argument-clause → ( ) | (function-call-argument-list)
function-call-argument-list → function-call-argument | function-call-argument ,function-call-argument-list
function-call-argument → expression | identifier  : expression
function-call-argument → operator | identifier : operator
­
trailing-closure → closure-expression­

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

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

выражение.init(аргументы инициализатора)

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

class SomeSubClass: SomeSuperClass {
    override init() {
        // инициализация подкласса
        super.init()
    }
}

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

// Аннотация типов обязательна, так как String имеет несколько инициализаторов.
let initializer: (Int) -> String = String.init
let oneTwoThree = [1, 2, 3].map(initializer).reduce("", combine: +)
print(oneTwoThree)
// Выведет "123"

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

let s1 = SomeType.init(data: 3)  // Действительный вариант
let s2 = SomeType(data: 1)       // Действительный вариант
 
let s3 = type(of: someValue).init(data: 7)  // Действительный вариант
let s4 = type(of: someValue)(data: 5)       // Ошибка

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

initializer-expression → postfix-expression­init­ initializer-expression → postfix-expression­init­argument-names­

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

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

выражение.имя члена

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

class SomeClass {
    var someProperty = 42
}
let c = SomeClass()
let y = c.someProperty  // доступ к члену

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

var t = (10, 20, 30)
t.0 = t.1
// Теперь t равно (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              // Двусмысленно
let b = instance.someMethod(x:y:)        // Недвусмысленно
 
let d = instance.overloadedMethod        // Двусмысленно
let d = instance.overloadedMethod(x:y:)  // Двусмысленно
let d: (Int, Bool) -> Void  = instance.overloadedMethod(x:y:)  // Недвусмысленно

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

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

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

explicit-member-expression → postfix-expression­decimal-digits­ explicit-member-expression → postfix-expression­identifier­generic-argument-clause­opt­ explicit-member-expression → postfix-expression­identifier­argument-names­ argument-names → argument-name­ argument-names­opt­ argument-name → identifier­

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

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

выражение.self
тип.self

Первый вариант вычисляет значение выражения. Например, x.self принимает значение x.

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

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

postfix-self-expression → postfix-expression­self­

Выражение сабскрипта

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

выражение[выражения индекса]

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

Информацию об объявлениях подстрочных индексов см. в Объявление протокола сабскрипта.

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

subscript-expression → postfix-expression­ [ ­expression-list­ 

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

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

выражение!

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

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

var x: Int? = 0
x! += 1
// x теперь 1
 
var someDictionary = ["a": [1, 2, 3], "b": [10, 20]]
someDictionary["a"]![0] = 100
// someDictionary теперь ["b": [10, 20], "a": [100, 2, 3]]

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

forced-value-expression → postfix-expression­

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

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

выражение?

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

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

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

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

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

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

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

func someFunctionWithSideEffects() -> Int {
   return 42  // фактически никаких побочных эффектов не имеет
}
var someDictionary = ["a": [1, 2, 3], "b": [10, 20]]
 
someDictionary["not here"]?[0] = someFunctionWithSideEffects()
// someFunctionWithSideEffects не вычисляется
// someDictionary все еще ["a": [1, 2, 3], "b": [10, 20]]
 
someDictionary["a"]?[0] = someFunctionWithSideEffects()
// someFunctionWithSideEffects вычисляется, возвращается 42
// someDictionary теперь равно ["a": [42, 2, 3], "b": [10, 20]]

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

optional-chaining-expression → postfix-expression­

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: