Закрытия
Закрытия являются автономными блоками функциональности, которая может раздаваться и использоваться в Вашем коде. Закрытия в Swift подобны блокам в C и Objective C и лямбдам в других языках программирования.
Закрытия могут получить и сохранить ссылки на любые константы и переменные от контекста, в котором они определяются. Это известно как закрывающийся по тем константам и переменным, отсюда имя «закрытия». Swift обрабатывает все управление памятью получения для Вас.
Глобальные и вложенные функции, как представлено в Функциях, являются фактически особыми случаями закрытий. Закрытия принимают одну из трех форм:
Глобальные функции являются закрытиями, имеющими имя и не получающими значений.
Вложенные функции являются закрытиями, которые имеют имя и могут получить значения от их функции включения.
Выражения закрытия являются закрытиями без имени, записанными в легком синтаксисе, который может получить значения от их окружающего контекста.
Выражения закрытия Swift имеют чистый, ясный стиль с оптимизацией, мотивирующей краткий, синтаксис без помех в общих сценариях. Эта оптимизация включает:
Выведение значения параметра и возвращаемого значения вводит от контекста
Неявные возвраты из закрытий отдельного выражения
Краткие имена параметра
Запаздывающий синтаксис закрытия
Выражения закрытия
Вложенные функции, столь же представленные во Вложенных функциях, являются удобными средними значениями именования и определения автономных блоков кода как часть большей функции. Однако иногда полезно записать более короткие версии подобных функции конструкций без полного объявления и имени. Это - особенно истина, когда Вы работаете с функциями, берущими другие функции в качестве один или больше их параметров.
Выражения закрытия являются способом записать встроенные закрытия в кратком, фокусируемом синтаксисе. Выражения закрытия обеспечивают несколько оптимизации синтаксиса для записи закрытий в сокращенной форме без потери ясности или намерения. Примеры выражения закрытия ниже иллюстрируют эту оптимизацию путем совершенствования единственного примера sorted
функционируйте по нескольким итерациям, каждая из которых выражает ту же функциональность более сжатым способом.
Сортированная функция
Стандартная библиотека Swift обеспечивает вызванную функцию sorted
, который сортирует массив значений известного типа, на основе вывода закрытия сортировки, которое Вы обеспечиваете. Как только это завершает процесс сортировки, sorted
функционируйте возвращает новый массив того же типа и размера как старый, с его элементами в корректном сортированном порядке. Исходный массив не изменяется sorted
функция.
Примеры выражения закрытия ниже используют sorted
функционируйте для сортировки массива String
значения в обратном алфавитном порядке. Вот начальный массив, который будет сортирован:
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
sorted
функция берет два параметра:
Массив значений известного типа.
Закрытие, берущее два параметра того же типа как содержание массива и возвращающее a
Bool
значение, чтобы сказать, должно ли первое значение появиться прежде или после второго значения один раз значения, сортируется. Закрытие сортировки должно возвратитьсяtrue
если первое значение должно появиться перед вторым значением, иfalse
иначе.
Этот пример сортирует массив String
значения, и таким образом, закрытие сортировки должно быть функцией типа (String, String) -> Bool
.
Один способ обеспечить закрытие сортировки состоит в том, чтобы записать нормальную функцию корректного типа, и передать его в как sorted
второй параметр функции:
func backwards(s1: String, s2: String) -> Bool {
return s1 > s2
}
var reversed = sorted(names, backwards)
// reversed is equal to ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
Если первая строка (s1
) больше, чем вторая строка (s2
), backwards
функция возвратится true
, указание этого s1
должен появиться прежде s2
в сортированном массиве. Для символов в строках, “больше, чем” означает, “появляется позже в алфавите, чем”. Это означает что буква "B"
“больше, чем” буква "A"
, и строка "Tom"
больше, чем строка "Tim"
. Это дает обратный алфавитный вид, с "Barry"
быть помещенным прежде "Alex"
, и т.д.
Однако это - довольно многоречивый способ записать то, что является по существу функцией отдельного выражения (a > b
). В этом примере было бы предпочтительно записать закрытию сортировки встроенный, использующий синтаксис выражения закрытия.
Синтаксис выражения закрытия
Синтаксис выражения закрытия имеет следующую общую форму:
{ (parameters) -> return type in
statements
}
Синтаксис выражения закрытия может использовать постоянные параметры, переменные параметры, и inout
параметры. Значения по умолчанию не могут быть предоставлены. Параметры Variadic могут использоваться, если Вы называете variadic параметр и помещаете его в последний раз в список параметров. Кортежи могут также использоваться в качестве типов параметра и типов возврата.
Пример ниже шоу версия выражения закрытия backwards
функция от ранее:
reversed = sorted(names, { (s1: String, s2: String) -> Bool in
return s1 > s2
})
Обратите внимание на то, что объявление параметров и типа возврата для этого встроенного закрытия идентично объявлению от backwards
функция. В обоих случаях это записано как (s1: String, s2: String) -> Bool
. Однако для встроенного выражения закрытия, параметры и тип возврата записаны в изогнутых фигурных скобках, не за пределами них.
Запуск организации закрытия представлен in
ключевое слово. Это ключевое слово указывает, что определение параметров закрытия и типа возврата закончилось, и организация закрытия собирается начаться.
Поскольку организация закрытия так коротка, это может даже быть записано на одной строке:
reversed = sorted(names, { (s1: String, s2: String) -> Bool in return s1 > s2 } )
Это иллюстрирует что полный вызов к sorted
функция осталась тем же. Пара круглых скобок все еще обертывает весь набор параметров за функцию. Однако одним из тех параметров является теперь встроенное закрытие.
Выведение типа от контекста
Поскольку закрытие сортировки передается как параметр функции, Swift может вывести типы своих параметров и тип значения, которое это возвращает из типа sorted
второй параметр функции. Этот параметр ожидает функцию типа (String, String) -> Bool
. Это означает что (String, String)
и Bool
типы не должны быть записаны как часть определения выражения закрытия. Поскольку все типы могут быть выведены, стрелка возврата (->
) и круглые скобки вокруг имен параметров могут также быть опущены:
reversed = sorted(names, { s1, s2 in return s1 > s2 } )
Всегда возможно вывести типы параметра и тип возврата при передаче закрытия функции как встроенное выражение закрытия. Когда закрытие используется в качестве аргумента функции, в результате Вы никогда не должны писать встроенное закрытие в его самой полной форме.
Тем не менее, можно все еще сделать типы явными, при необходимости и выполнение так мотивировано, если оно избегает неоднозначности для читателей Вашего кода. В случае sorted
функция, цель закрытия ясна из факта, что сортировка имеет место, и безопасно для читателя предположить, что закрытие, вероятно, будет работать с String
значения, потому что это помогает с сортировкой массива строк.
Неявные возвраты из закрытий отдельного выражения
Закрытия отдельного выражения могут неявно возвратить результат своего отдельного выражения путем исключения return
ключевое слово от их объявления, как в этой версии предыдущего примера:
reversed = sorted(names, { s1, s2 in s1 > s2 } )
Здесь, функциональный тип sorted
второй параметр функции проясняет это a Bool
значение должно быть возвращено закрытием. Поскольку организация закрытия содержит отдельное выражение (s1 > s2
) это возвращает a Bool
значение, нет никакой неоднозначности, и return
ключевое слово может быть опущено.
Краткие имена параметра
Swift автоматически обеспечивает краткие имена параметра для встраивания закрытий, которые могут использоваться для обращения к значениям параметров закрытия именами $0
, $1
, $2
, и т.д.
При использовании этих кратких имен параметра в выражении закрытия можно опустить список аргументов закрытия из его определения, и число и тип кратких имен параметра будут выведены из ожидаемого функционального типа. in
ключевое слово может также быть опущено, потому что выражение закрытия составлено полностью его организации:
reversed = sorted(names, { $0 > $1 } )
Здесь, $0
и $1
обратитесь к закрытию, первому и второму String
параметры.
Функции оператора
Существует фактически еще более короткий способ записать выражение закрытия выше. Swift String
тип определяет свою специфичную для строки реализацию большего - чем оператор (>
) как функция, имеющая два параметра типа String
, и возвращает значение типа Bool
. Это точно соответствует функциональный тип, необходимый для sorted
второй параметр функции. Поэтому можно просто передать в большем - чем оператор, и Swift выведет, что Вы хотите использовать его специфичную для строки реализацию:
reversed = sorted(names, >)
Для больше о функциях оператора, посмотрите Функции Оператора.
Запаздывающие закрытия
Если необходимо передать выражение закрытия функции как заключительный параметр функции, и выражение закрытия длинно, может быть полезно записать его как запаздывающее закрытие вместо этого. Запаздывающее закрытие является выражением закрытия, записанным за пределами (и после) круглые скобки вызова функции, который оно поддерживает:
func someFunctionThatTakesAClosure(closure: () -> ()) {
// function body goes here
}
// here's how you call this function without using a trailing closure:
someFunctionThatTakesAClosure({
// closure's body goes here
})
// here's how you call this function with a trailing closure instead:
someFunctionThatTakesAClosure() {
// trailing closure's body goes here
}
Сортирующее строку закрытие от раздела Closure Expression Syntax выше может быть записано за пределами sorted
круглые скобки функции как запаздывающее закрытие:
reversed = sorted(names) { $0 > $1 }
Запаздывающие закрытия являются самыми полезными, когда закрытие достаточно долго, что не возможно записать, что это встраивает на одной строке. Как пример, Swift Array
тип имеет a map(_:)
метод, берущий выражение закрытия в качестве его отдельного аргумента. Закрытие вызывают один раз для каждого элемента в массиве и возвращается, альтернатива отобразила значение (возможно некоторого другого типа) для того элемента. Природу отображения и тип возвращенного значения оставляют до закрытия указать.
После применения предоставленного закрытия к каждому элементу матрицы, map(_:)
метод возвращает новый массив, содержащий все новые отображенные значения в том же порядке как их соответствующие значения в исходном массиве.
Вот то, как можно использовать map(_:)
метод с запаздывающим закрытием для преобразования массива Int
значения в массив String
значения. Массив [16, 58, 510]
используется для создания нового массива ["OneSix", "FiveEight", "FiveOneZero"]
:
let digitNames = [
0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four",
5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
]
let numbers = [16, 58, 510]
Код выше создает словарь отображений между целочисленными цифрами и англоязычными версиями их имен. Это также определяет массив целых чисел, готовых быть преобразованными в строки.
Можно теперь использовать numbers
массив для создания массива String
значения, путем передачи выражения закрытия массиву map(_:)
метод как запаздывающее закрытие. Обратите внимание на то, что вызов к numbers.map
не должен включать круглые скобки после map
, потому что map(_:)
метод имеет только один параметр, и тот параметр предоставлен как запаздывающее закрытие:
let strings = numbers.map {
(var number) -> String in
var output = ""
while number > 0 {
output = digitNames[number % 10]! + output
number /= 10
}
return output
}
// strings is inferred to be of type [String]
// its value is ["OneSix", "FiveEight", "FiveOneZero"]
map(_:)
вызовы метода выражение закрытия один раз для каждого элемента в массиве. Вы не должны указывать тип входного параметра закрытия, number
, потому что тип может быть выведен из значений в массиве, который будет отображен.
В этом примере, закрытие number
параметр определяется как переменный параметр, как описано в Константе и Переменные Параметры, так, чтобы значение параметра могло быть изменено в организации закрытия, вместо того, чтобы объявить новую локальную переменную и присвоить переданный number
оцените ему. Выражение закрытия также указывает тип возврата String
, указать тип, который будет сохранен в отображенном выходном массиве.
Выражение закрытия создает вызванную строку output
каждый раз это вызывают. Это вычисляет последнюю цифру number
при помощи оператора остатка (number % 10
), и использование эта цифра для поиска надлежащей строки в digitNames
словарь. Закрытие может использоваться для создания строкового представления любого целого числа, больше, чем нуль.
Строка, полученная от digitNames
словарь добавляется к передней стороне output
, эффективно создавая строковую версию числа наоборот. (Выражение number % 10
дает значение 6
для 16
, 8
для 58
, и 0
для 510
.)
number
переменная тогда разделена на 10
. Поскольку это - целое число, это округляется в меньшую сторону во время подразделения, таким образом, 16
становится 1
, 58
становится 5
, и 510
становится 51
.
Процесс повторяется до number /= 10
равно 0
, в которой точке output
строка возвращается закрытием и добавляется к выходному массиву map
функция.
Использование запаздывающего синтаксиса закрытия в примере выше аккуратно сразу инкапсулирует функциональность закрытия после функции, которую закрытие поддерживает, не будучи должен обернуть все закрытие в map
внешние круглые скобки функции.
Получение значений
Закрытие может получить константы и переменные от окружающего контекста, в котором оно определяется. Закрытие может тогда относиться к и изменить значения тех констант и переменных из его организации, даже если больше, существует исходный объем, не определивший константы и переменные.
В Swift самая простая форма закрытия, которое может получить значения, является вложенной функцией, записанной в организации другой функции. Вложенная функция может получить любой из параметров своей внешней функции и может также получить любые константы и переменные, определенные во внешней функции.
Вот пример вызванной функции makeIncrementer
, который содержит вызванную вложенную функцию incrementer
. Вложенный incrementer
функционируйте получает два значения, runningTotal
и amount
, от его окружающего контекста. После получения этих значений, incrementer
возвращается makeIncrementer
как постепенно увеличивающееся закрытие runningTotal
amount
каждый раз это вызывают.
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
Тип возврата makeIncrementer
() -> Int
. Это означает, что возвращает функцию, а не простое значение. Функция, которую это возвращает, не имеет никаких параметров и возвращается Int
оцените каждый раз, когда это вызывают. Чтобы изучить, как функции могут возвратить другие функции, посмотрите Функциональные Типы как Типы Возврата.
makeIncrementer
функция определяет вызванную целочисленную переменную runningTotal
, сохранить текущее рабочее общее количество инкрементора, который будет возвращен. Эта переменная инициализируется со значением 0
.
makeIncrementer
функция имеет сингл Int
параметр с внешним именем forIncrement
, и локальное имя amount
. Значение аргумента, переданное этому параметру, указывает сколько runningTotal
должен быть постепенно увеличен к каждому разу, когда возвращенная функция инкрементора вызвана.
makeIncrementer
определяет вызванную вложенную функцию incrementer
, который выполняет фактическое постепенное увеличение. Эта функция просто добавляет amount
к runningTotal
, и возвращает результат.
Когда рассмотрено в изоляции, вложенном incrementer
функция могла бы казаться необычной:
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
incrementer
функция не имеет никаких параметров, и все же она относится к runningTotal
и amount
из его тела функции. Это делает это путем получения существующих значений runningTotal
и amount
от его окружающей функции и использования их в его собственном теле функции.
Поскольку это изменяет runningTotal
переменная каждый раз это вызывают, incrementer
получает ссылку на ток runningTotal
переменная, и не только копия ее начального значения. Получение ссылки гарантирует это runningTotal
не исчезает когда вызов к makeIncrementer
концы, и гарантируют это runningTotal
доступно в следующий раз incrementer
функция вызвана..
Однако, потому что это не изменяет amount
, и amount
не видоизменен снаружи, incrementer
фактически получения и хранилища копия значения, сохраненного в amount
. Это значение сохранено вместе с новым incrementer
функция.
Вот пример makeIncrementer
в действии:
let incrementByTen = makeIncrementer(forIncrement: 10)
Этот пример устанавливает вызванную константу incrementByTen
относиться к добавляющей функции инкрементора 10
к runningTotal
переменная каждый раз это вызывают. Вызывание функции многократно показывает это поведение в действии:
incrementByTen()
// returns a value of 10
incrementByTen()
// returns a value of 20
incrementByTen()
// returns a value of 30
При создании второго инкрементора он будет иметь свою собственную сохраненную ссылку на новое, отдельное runningTotal
переменная:
let incrementBySeven = makeIncrementer(forIncrement: 7)
incrementBySeven()
// returns a value of 7
Вызов исходного инкрементора (incrementByTen
) снова продолжает постепенно увеличивать его собственное runningTotal
переменная, и не влияет на переменную, полученную incrementBySeven
:
incrementByTen()
// returns a value of 40
Закрытия являются ссылочными типами
В примере выше, incrementBySeven
и incrementByTen
константы, но закрытия, к которым относятся эти константы, все еще в состоянии постепенно увеличиться runningTotal
переменные, которые они получили. Это вызвано тем, что функции и закрытия являются ссылочными типами.
Каждый раз, когда Вы присваиваете функцию или закрытие к константе или переменной, Вы фактически устанавливаете ту константу или переменные, чтобы быть ссылкой на функцию или закрытие. В примере выше, это - выбор закрытия это incrementByTen
относится к этому, является постоянным, а не содержание самого закрытия.
Это также означает, что при присвоении закрытия двум различным константам или переменным обе из тех констант или переменных будут относиться к тому же закрытию:
let alsoIncrementByTen = incrementByTen
alsoIncrementByTen()
// returns a value of 50