Замыкания Swift

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

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

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

Формы замыканий:

Глобальные функции Вложенные функции Замыкающие выражения
Имеют имена, но не могут захватывать никакие значения. Имеют имена и могут захватывать значения из своей охватывающей функции. Безымянные замыкания, использующие облегчённый синтаксис, могут захватывать значения в зависимости от окружающего контекста.

Замыкания в Swift имеют множество оптимизаций:

  1. Выводить типы параметров и возвращаемого значения из контекста
  2. Неявный возврат из замыканий с одним выражением (то есть если тело замыкания содержит только одну строку кода, return можно опустить)
  3. Можно использовать упрощённые имена параметров, например $0, $1 (начиная с 0, обозначают первый, второй параметры...)
  4. Предоставляет синтаксис завершающего замыкания
  5. Синтаксис

    Ниже определяется синтаксис замыкания, которое принимает параметры и возвращает указанный тип:

    {(parameters) -> return type in
       statements
    }
    

    Пример

    import Cocoa
    
    let studname = { print("Swift 闭包实例。") }
    studname()
    

    Вывод выполнения вышеуказанной программы:

    Swift 闭包实例。
    

    Следующая форма замыкания принимает два параметра и возвращает логическое значение:

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

    Пример

    import Cocoa
    
    let divide = {(val1: Int, val2: Int) -> Int in 
       return val1 / val2 
    }
    let result = divide(200, 20)
    print (result)
    

    Вывод выполнения вышеуказанной программы:

    10
    

    Замыкающие выражения

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


    Метод 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 автоматически предоставляет сокращённые имена аргументов для встроенных замыканий. Вы можете напрямую обращаться к параметрам замыкания по порядку, используя $0, $1, $2.

    Пример

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

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

    Вывод выполнения вышеуказанной программы:

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

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


    Операторные функции

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

    В SwiftStringтип определяет строковую реализацию оператора «больше» (>), который, как функция, принимает дваStringпараметра типа и возвращаетBoolзначение типа.sort(_:)соответствует типу функции, требуемому вторым параметром метода.

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

    Вывод выполнения вышеуказанной программы:

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

    Замыкающие замыкания

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

    func someFunctionThatTakesAClosure(closure: () -> Void) {
        // 函数体部分
    }
    
    // 以下是不使用尾随闭包进行函数调用
    someFunctionThatTakesAClosure({
        // 闭包主体部分
    })
    
    // 以下是使用尾随闭包进行函数调用
    someFunctionThatTakesAClosure() {
      // 闭包主体部分
    }
    

    Пример

    import Cocoa
    
    let names = ["AT", "AE", "D", "S", "BE"]
    
    //尾随闭包
    var reversed = names.sorted() { $0 > $1 }
    print(reversed)
    

    Выражение { $0 > $1 } после sort() — это хвостовое замыкание.

    Вывод выполнения вышеуказанной программы:

    ["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
    Другие расширения