Czym jest zamknięcie?
sobes.tech AI
Odpowiedź od AI
Zamknięcie (closure) w Swift to samodzielny blok funkcjonalności ({}), który może być przekazany i używany w kodzie. Zamknięcia mogą przechwytywać i przechowywać odniesienia do dowolnych stałych i zmiennych z kontekstu, w którym są zdefiniowane.
Przykłady użycia:
- Jako argumenty funkcji (np. obsługiwacze zakończenia).
- Do definiowania zachowania podczas iteracji kolekcji (np. używając metod
map,filter,reduce,sorted). - Do opóźnionego wykonania kodu.
Składnia:
// Podstawowa składnia
{ (parametry) -> typZwracany in
instrukcje
}
// Przykład zamknięcia przyjmującego Int i zwracającego String
let integerToString: (Int) -> String = { (number: Int) -> String in
return "Liczba: \(number)"
}
Cechy:
- Automatyczne przechwytywanie zmiennych.
- Możliwość definiowania zamknięć bez jawnego określenia typów parametrów i wartości zwracanej (cukier składniowy).
- Użycie skróconych nazw parametrów ($0, $1, itd.).
- Składnia zamknięcia z trailing closure.
Przechwytywanie zmiennych:
Zamknięcie może przechwycić zmienne z otoczenia. Domyślnie przechwytywanie odbywa się przez odniesienie. Jeśli zamknięcie jest używane po tym, jak zmienna źródłowa opuściła zakres, nadal zachowuje dostęp do niej.
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0 // Zmienna z zewnętrznego kontekstu
let incrementer: () -> Int = { // Zamknięcie
runningTotal += amount // Przechwytuje runningTotal i amount
return runningTotal
}
return incrementer
}
let incrementByTen = makeIncrementer(forIncrement: 10)
print(incrementByTen()) // Wypisze: 10
print(incrementByTen()) // Wypisze: 20
Listy przechwytywania (Capture Lists):
Służą do jawnego określenia, jak zmienne mają być przechwytywane - przez wartość lub przez odniesienie, a także do unikania cykli silnych odniesień (retain cycles).
class MyClass {
var value = 0
lazy var myClosure: () -> Void = { [self, value] in // Przechwytuje self przez odniesienie, value przez wartość
print("Wartość self.value: \(self.value)")
print("Przechwycona wartość: \(value)")
}
deinit {
print("Instancja MyClass zwolniona")
}
}
Aby zapobiec cyklom silnych odniesień podczas przechwytywania obiektów klas, używa się składni [weak self] lub [unowned self].
class ViewController {
var dataFetcher: DataFetcher?
func fetchData() {
dataFetcher?.fetch { [weak self] result in // Słabe przechwycenie self
guard let self = self else { return } // Sprawdzenie istnienia self
// Obsługa result
}
}
}
class DataFetcher {
var completionHandler: ((Result<String, Error>) -> Void)?
func fetch(completion: @escaping (Result<String, Error>) -> Void) {
self.completionHandler = completion
// Asynchroniczna operacja...
}
}