Замыкания
Замыкания
«Замыкания» — бесплатный урок Swift Academy на CoddyKit. Это урок 1 из 1. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Swift Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Swift Academy содержит 1 уроков всего.
Введение
Здравствуйте!
Замыкания — это самостоятельные блоки кода. Замыкание можно передать в код как параметр или использовать внутри другого кода.
Замыкание — это разновидность функции. Его можно передавать в код. Замыкания похожи на лямбда-выражения в других языках программирования.
Управление памятью
Один из важнейших моментов заключается в том, что замыкание может хранить значения констант и переменных из контекста, в котором оно определено.
Такое хранение называется захватом контекста.
Swift управляет памятью во время этого хранения.
Виды замыканий
Существуют разные виды замыканий.
- Глобальные функции — это именованные замыкания. Они не хранят никаких значений.
- Вложенные функции могут захватывать значения из внешней функции.
- Выражение замыкания — это анонимное замыкание, которое с помощью простого синтаксиса может хранить данные окружающего контекста.
Синтаксис замыканий
Синтаксис замыканий стараются максимально упростить. Наиболее важные особенности перечислены ниже.
- Типы параметров в замыканиях могут выводиться автоматически.
- Возвращаемые значения могут выводиться автоматически.
- Замыкание может возвращать значение в одной строке.
- Имена аргументов можно указывать в сокращённом виде, например $ 0 или $ 1.
- Можно использовать синтаксис завершающего замыкания.
Информация
Выражения замыканий позволяют записывать замыкания непосредственно в месте использования, уделяя особое внимание синтаксису и простоте.
Синтаксис замыкания
Ниже показан синтаксис замыкания.
{ (parameters) -> return type in
statements
}
func backward(_ s1: String, _ s2: String) -> Bool {
return s1 > s2
}
//If we didn't use closures, our code structure would be as follows.
var reversedNames = names.sorted(by: backward)
Синтаксис замыкания
Однако при использовании замыкания не нужно без необходимости определять глобальное замыкание с именем.
Достаточно определить в параметре простое выражение замыкания. Рассмотрим пример.
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
let reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 > s2
})
print(reversedNames)
///sorted method takes closure as parameter.Ключевое слово начала тела
По ключевому слову начала тела можно понять, где начинается тело замыкания.
Перед этим ключевым словом необходимо указать параметры, получаемые замыканием, и возвращаемый тип.
Выведение типа из контекста
Выведение типов из контекста
В приведённом выше примере типы параметров и возвращаемый тип выражения замыкания можно определить автоматически, поскольку метод sorted принимает замыкание.
Поэтому нет необходимости указывать их отдельно.
В этом примере параметры замыкания будут иметь строковый тип, поскольку значения в массиве имён являются строками.
Аналогично, для сортировки замыканию нужен логический тип, поэтому возвращаемый тип будет определён как логический.
Пример
Это упростит приведённую ниже запись.
Стрелку также можно убрать из кода, если возвращаемый тип выводится автоматически.
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
let reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 > s2
})
print(reversedNames)Краткий синтаксис
Использование полной записи вместо краткой предпочтительнее, чтобы не запутать читателей кода.
Приведённый выше синтаксис можно сократить ещё сильнее.
Возвращаемое значение можно записать короче, убрав ключевое слово возврата из выражения замыкания.
/*
In this example, the return keyword has been extracted.
It already has single expression.
There is no need to express it as an extra with return.
*/
var names = ["Coddy", "Kit"]
let reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
print(reversedNames)Пример
В этом примере ключевое слово возврата опущено.
Здесь всего одно выражение, поэтому нет необходимости дополнительно указывать возврат.
reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )Сокращённые имена
Синтаксис можно сократить ещё сильнее. В примере выше мы указали, что замыкание принимает два параметра. Их типы выводятся автоматически.
Теперь можно не описывать эти параметры и не тратить время на их явное указание.
Кроме того, поскольку описание параметров и возвращаемого значения отсутствует, ключевое слово начала тела также можно опустить.
let names = ["Coddy", "Kit"]
let reversedNames = names.sorted(by: { $0 > $1 } )
print(reversedNames)
// In this example $0 will shortcut the first parameter and $1 will shortcut the second parameter.Сокращённый пример
var names = ["Coddy", "Kit"]
let reversedNames = names.sorted(by: >)
print(reversedNames)
// Greater than operator takes two parameters and the rotation type is squat.TRAILING CLOSURE
Завершающее замыкание
Если замыкание, передаваемое функции, является её последним аргументом, а выражение замыкания получается длинным, может быть удобнее записать его как завершающее замыкание.
Синтаксис завершающего замыкания можно использовать как синтаксический сахар. Он повышает читаемость кода.
Последний параметр большинства методов iOS — замыкание.
Последний параметр
Например, в этом примере последний параметр — замыкание.
class Animator {
class func animate(withDuration duration: Double, animations: () -> Void) {
animations()
}
}
class MyViewController {
func start() {
// With the traditional method, we can call this fund as follows.
Animator.animate(withDuration: 1, animations: { [unowned self] in
print("red")
})
}
}
MyViewController().start()Поскольку приведённый выше код менее удобен для чтения, его можно записать так
Поскольку приведённый выше код менее удобен для чтения, его можно записать так.
import Foundation
Thread.sleep(forTimeInterval: 1)
print("Background color changed to red")Пример
При использовании синтаксиса завершающего замыкания не нужно указывать метку аргумента.
Это связано с тем, что замыкание теперь стало частью вызова функции.
func someFunctionThatTakesAClosure(closure: () -> Void) {
// function body goes here
}
// Here's how you call this function without using a trailing closure:
someFunctionThatTakesAClosure(closure: {
// closure's body goes here
})
// Here's how you call this function with a trailing closure instead:
someFunctionThatTakesAClosure() {
// trailing closure's body goes here
}Синтаксис
На самом деле синтаксис завершающего замыкания можно считать синтаксическим сахаром. Он делает код более читаемым.
/*
In the example below, animations
are expressed as closure.
*/
public class func animate(withDuration: TimeInterval, animations: () -> Void)
// In traditional usage, we need to make a call like the following.
UIView.animate(withDuration: 1, animations: { [unowned self] in
self.view.backgroundColor = UIColor.red
})
/*
In this way, both the readability of the code
increases and we avoid the use of extra brackets.
If it is as above and the last parameter of the method
is closure, Swift allows to make a call like the one below.
*/
UIView.animate(withDuration: 1) { [unowned self] in
self.view.backgroundColor = UIColor.red
}Для проверки кода можно использовать код ниже
Для проверки кода можно использовать код ниже.
func greetThenRunClosure(name: String, closure: () -> ()) {
print("Hello, \(name)!")
closure()
}
greetThenRunClosure(name: "Paul") {
print("The closure was run")
}Захват значений замыканием
Захват значений замыканием
Замыкание может хранить значения переменных из окружающего его контекста. Более того, значения этих переменных можно изменять из тела самого замыкания и обращаться к ним.
Даже если они находятся не в исходной области видимости, в которой были определены.
Продолжение
Как правило, эти данные можно хранить во вложенных функциях и выражениях замыкания. Они могут сохранять данные внешней функции.
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
let incrementByTen = makeIncrementer(forIncrement: 10)
print(incrementByTen())
// returns a value of 10
print(incrementByTen())
// returns a value of 20
print(incrementByTen())
// returns a value of 30Продолжение
Если присвоить замыкание свойству класса, оно начнёт удерживать экземпляр, которому принадлежит.
В результате между замыканием и экземпляром образуется сильный цикл ссылок.
Особое внимание следует уделять утечкам памяти.
Предупреждение
Замыкания и функции — ссылочные типы.
@escaping
Такое использование может потребоваться во многих случаях, когда замыкание выполняется асинхронно.
В ранних версиях Swift (1 и 2) замыкания в параметрах Swift были выходящими, а позднее стали невыходящими. Если требовалось передать невыходящее замыкание, это нужно было указывать явно.
В следующих версиях параметры-замыкания сделали невыходящими, чтобы экономить память.
Теперь, если нужно определить выходящее замыкание, следует указать ключевое слово @escaping.
@ESCAPING: ключевое слово
@ESCAPING: ключевое слово
Если необходимо вызывать замыкания, переданные функции, после её завершения, следует пометить параметр ключевым словом @escaping.
Замыкания, передаваемые функции по умолчанию, являются ссылочными типами, но не увеличивают счётчик ссылок. После вызова выполнение такого замыкания завершается, поэтому дополнительно увеличивать счётчик ссылок не требуется.
Однако если замыкание должно использоваться как параметр после вызова функции, его, возможно, потребуется обозначить ключевым словом @escaping.
nonEscaping
Сначала посмотрим, как работает замыкание nonEscaping.
Когда это замыкание передаётся функции как параметр, оно выполняется в теле функции, после чего выполнение функции завершается.
После возврата функции, то есть после завершения выполнения, замыкание выходит за пределы своей области видимости и освобождается из памяти.
Использование выходящего замыкания
Использование выходящего замыкания
Когда замыкание передаётся как аргумент, оно продолжает храниться, даже после возврата функции и завершения выполнения.
Оно занимает место в памяти до момента выполнения замыкания. Существует несколько видов выходящих замыканий.
Поздравляем
Поздравляем 🎉
Мы подошли к концу урока.
До встречи на следующем уроке 😎

Часто задаваемые вопросы
Урок «Замыкания» бесплатный?
Да — полный текст урока «Замыкания» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Swift Academy, подпишись на CoddyKit PRO. Курс Swift Academy содержит 1 уроков всего.
Чему я научусь в уроке «Замыкания»?
Замыкания Ты практикуешь Swift Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Swift Academy?
Предыдущий опыт не требуется. Swift Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 1.
Сколько времени занимает урок «Замыкания»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Swift Academy?
Да. Каждый урок Swift Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.