English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Основной курс Swift

Операторы Swift

Swift Управление потоком

Swift Коллекции

Swift Функции

Swift Уровень 2

Клозуры в Swift

В этой статье вы узнаете, что такое closures, их грамматика и типы closures в Swift.

В статье о функции Swift мы использовали ключевое слово func для создания функции. Однако в Swift есть и другой особый тип функции, называемый closure, который можно определить без использования ключевого слова func и имени функции.
Как и функции, closures могут принимать параметры и возвращать значения. Они также содержат набор инструкций, которые выполняются после вызова и могут быть назначены переменным или константам как функции.

Closures в Swift похожи на блоки кода в C и Objective-C, а также на анонимные функции в других языках программирования.

Глобальные функции и вложенные функции на самом деле являются особыми видами closures.

Формы closures:

Глобальные функцииВложенные функции闭包表达式
Именованные closures, которые не могут захватывать никакие значения.Именованные closures, также могут捕获 значения из окружения функции.Безымянные closures, использующие легковесную грамматику, могут捕获 значения из окружения.

В Swift closures имеют много оптимизированных мест:

  • Дедукция типов параметров и возвращаемых значений из контекста

  • Импlicit return из выражений, заключенных в скобки (т.е. в теле closures всего один код, который можно опустить)

  • 可以使用简化参数名,如$0, $1(从0开始,表示第i个参数...)}

  • 提供了尾随闭包语法(Trailing closure syntax)

语法

以下定义了一个接收参数并返回指定类型的闭包语法:

{(parameters) -> return type in
   statements
}

Онлайн пример

let simpleClosure = {
    print("Hello, World!")
}
simpleClosure()

Результат выполнения программы выше:

Hello, World!

以下闭包形式接收两个参数并返回布尔值:

{(Int, Int) -> Bool in
   Statement1
   Statement 2
    ---
   Statement n
}

Онлайн пример

let simpleClosure:(String) -> (String) = { name in
    
    let greeting = "Hello, World! " + "Program"
    return greeting
}
let result = simpleClosure("Hello, World")
print(result)

Результат выполнения программы выше:

Hello, World! Program

闭包表达式

闭包表达式是一种利用简洁语法构建内联闭包的方式。闭包表达式提供了一些语法优化,使得撰写闭包变得简单明了。

sorted 方法

Swift 标准库提供了名为 sorted(by:) 的方法,会根据您提供的用于排序的闭包函数将已知类型数组中的值进行排序。

排序完成后,sorted(by:) 方法会返回一个与原数组大小相同,包含同类型元素且元素已正确排序的新数组。原数组不会被 sorted(by:) 方法修改。

sorted(by:)方法需要传入两个参数:

  • 已知类型的数组

  • 闭包函数,该闭包函数需要传入与数组元素类型相同的两个值,并返回一个布尔类型值来表明当排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值前面,排序闭包函数需要返回 true,反之返回 false

Онлайн пример

import Cocoa
let names = ["AT", "AE", "D", "S", "BE"]
// 使用普通函数(或内嵌函数)提供排序功能,闭包函数类型需为(String, String) -> Bool。
func backwards(s1: String, s2: String) -> Bool {
    return s1 > s2
}
var reversed = names.sorted(by: backwards)
print(reversed)

Результат выполнения программы выше:

["S", "D", "BE", "AT", "AE"]

Если первый строковый символ (s1) больше второго строкового символа (s2), функция backwards возвращает true, что означает, что s1 должен出现在 s2 впереди в новом массиве. "Больше" для символов в строке означает "позднее的出现顺序". Это означает, что литера "B" больше литеры "A", строка "S" больше строки "D". Это будет выполнено в обратном алфавитном порядке, "AT" будет排在 "AE" впереди.

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

Swift автоматически предоставляет возможность сокращения имен параметров для инлайновых функций, и вы можете напрямую вызывать параметры closures в порядке через $0, $1, $2.

Онлайн пример

import Cocoa
let names = ["AT", "AE", "D", "S", "BE"]
var reversed = names.sorted(by: { $0 > $1 })
print(reversed)

$0 и $1 представляют собой первый и второй параметры типа String в closures.

Результат выполнения программы выше:

["S", "D", "BE", "AT", "AE"]

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

функции运算атора

на самом деле есть более короткий способ записи выражения closures в примере выше.

функции оператора SwiftStringтип определяет информацию о знаке > (>) строковое представление, которое является функцией, принимающей дваStringтипу параметра и возвращаетBoolтипам значений. Это именно то, что соответствуетsort(_):Второй параметр метода требует соответствующего типа функции. Таким образом, вы можете просто передать大于 знак, и Swift автоматически определит, что вы хотите использовать строковую функцию с этим знаком:

import Cocoa
let names = ["AT", "AE", "D", "S", "BE"]
var reversed = names.sorted(by: >)
print(reversed)

Результат выполнения программы выше:

["S", "D", "BE", "AT", "AE"]

Следование closures

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

func someFunctionThatTakesAClosure(closure: () -> Void) {
    // Основная часть функции
}
// Вот пример вызова функции без использования следования closures
someFunctionThatTakesAClosure({
    // Основная часть блока closures
}
// Вот пример вызова функции с использованием следования closures
someFunctionThatTakesAClosure() {
  // Основная часть блока closures
}

Онлайн пример

import Cocoa
let names = ["AT", "AE", "D", "S", "BE"]
//Следование закрытых функций
var reversed = names.sorted() { $0 > $1 }
print(reversed)

sort() после { $0 > $1} является следствием закрытой функции.

Результат выполнения программы выше:

["S", "D", "BE", "AT", "AE"]

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

reversed = names.sorted { $0 > $1 }

Захваченные значения

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

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

Самый простой вид закрытых функций в Swift - это вложенные функции, которые определяются в теле другой функции.

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

Посмотрите на этот пример:

func makeIncrementor(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementor() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementor
}

Функция makeIncrementor, которая имеет параметр типа Int amout и имеет внешнюю переменную forIncremet, что означает, что при вызове вам необходимо использовать это внешнее имя. Возвращает-> IntФункции.

В теле функции объявлены переменная runningTotal и функция incrementor.

Функция incrementor не принимает никаких параметров, но в теле функции доступ к переменным runningTotal и amount. Это потому, что она реализована через захват переменных runningTotal и amount, существовавших в теле функции, которая их содержала.

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

Таким образом, при вызове этой функции будет производиться накопление:

import Cocoa
func makeIncrementor(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementor() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementor
}
let incrementByTen = makeIncrementor(forIncrement: 10)
// Возвращенное значение равно 10
print(incrementByTen())
// Возвращенное значение равно 20
print(incrementByTen())
// Возвращенное значение равно 30
print(incrementByTen())

Результат выполнения программы выше:

10
20
30

Закрытые функции являются типом ссылок

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

Это потому, что функции и закрытые функции都属于 типам ссылок.

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

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

import Cocoa
func makeIncrementor(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementor() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementor
}
let incrementByTen = makeIncrementor(forIncrement: 10)
// Возвращенное значение равно 10
incrementByTen()
// Возвращенное значение равно 20
incrementByTen()
// Возвращенное значение равно 30
incrementByTen()
// Возвращенное значение равно 40
incrementByTen()
let alsoIncrementByTen = incrementByTen
// Возвращенное значение также равно 50
print(alsoIncrementByTen())

Результат выполнения программы выше:

50