По умолчанию все closure параметры в Swift являются non-escaping. Это означает:
func performOperation(with completion: () -> Void) {
// Замыкание выполняется в рамках функции
completion()
// После выполнения функция завершается
}
Помечается с @escaping
и может:
self
(при использовании в классах)class DataManager {
var completionHandlers: [() -> Void] = []
func storeCompletion(_ completion: @escaping () -> Void) {
// Сохраняем для последующего использования
completionHandlers.append(completion)
}
}
func fetchData(completion: @escaping (Result<Data, Error>) -> Void) {
URLSession.shared.dataTask(with: url) { data, _, error in
// Выполняется после возврата из fetchData
if let error = error {
completion(.failure(error))
} else if let data = data {
completion(.success(data))
}
}.resume()
}
func animateView(_ view: UIView, completion: @escaping () -> Void) {
UIView.animate(withDuration: 0.3, animations: {
view.alpha = 0
}, completion: { _ in
// Вызывается после завершения анимации
completion()
})
}
class MyClass {
var value = 10
func doWork() {
storeCompletion { [weak self] in
// Обязательно weak/unowned!
self?.value = 20
}
}
}
Non-escaping:
Escaping:
Начиная с Swift 5, closure в не-escaping позициях могут иногда автоматически становиться escaping при необходимости:
func execute(_ block: () -> Void,
concurrentlyWith other: () -> Void) {
// block может вести себя как escaping внутри
DispatchQueue.global().async(execute: block)
other()
}
понимание различий между escaping и non-escaping closure критически важно для безопасной работы с памятью и асинхронными операциями в Swift. Non-escaping - выбор по умолчанию для большинства операций, тогда как escaping необходимо явно указывать для асинхронных задач и сохранения замыканий. Всегда учитывайте риски циклических ссылок при работе с escaping closure.