Sobes.tech
Junior

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...
    }
}