Zamknięcie escaping to zamknięcie, które "uchodzi" z obszaru widoczności wywołującej funkcji (na przykład, jest przechowywane do asynchronicznego wykonania).
Domyślnie w Swift funkcje przyjmują zamknięcia non-escaping: zamknięcie jest wykonywane w obrębie wywołania funkcji.
Aby wyraźnie oznaczyć escaping, używa się słowa kluczowego @escaping:
func asyncTask(completion: @escaping () -> Void) { DispatchQueue.main.async { completion() } }
Kluczowe różnice:
[weak self] lub [unowned self], aby zapobiec wyciekom pamięci (retain cycle).Czy zawsze należy pisać @escaping dla zamknięć przekazywanych jako parametry do funkcji wywołującej DispatchQueue.async?
— Tak. Ponieważ DispatchQueue.async przechowuje zamknięcie do momentu wykonania, zamknięcie staje się escaping.
Przykład:
func foo(action: () -> Void) { DispatchQueue.main.async { action() // Nie skompiluje się: zamknięcie musi być escaping. } } // Należy: func bar(action: @escaping () -> Void) { ... }
Historia
Kontroler stworzył silne odniesienie do self wewnątrz zamknięcia escaping (na przykład, zapytanie sieciowe). Kontroler nie został zwolniony po zejściu z ekranu — silny cykl zatrzymania. Rozwiązanie: używać [weak self] lub [unowned self].
Historia
Funkcja przekazywała zamknięcie do DispatchQueue.async, ale nie oznaczyła go jako escaping. Projekt nie kompilował się, a błędy było trudno znaleźć z powodu zagnieżdżenia funkcji.
Historia
Wewnątrz zamknięcia odwoływano się do obiektu, który już został zdeinicjowany w momencie wywołania zamknięcia (używaliśmy [unowned self]). W wyniku tego — awaria w czasie wykonywania. Rozwiązanie: używać [weak self] oraz sprawdzenia na nil.