programowanieProgramista iOS

Co to są zamknięcia escaping i non-escaping w Swift? Jak prawidłowo zorganizować pracę z zamknięciami, jakie są niuanse ich użycia i czym może to grozić przy pracy z kodem asynchronicznym?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

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:

  • Zamknięcia escaping mogą być przechowywane i wywoływane później.
  • Aby uchwycić self w zamknięciu escaping - należy jawnie określić [weak self] lub [unowned self], aby zapobiec wyciekom pamięci (retain cycle).

Pytanie z pułapką.

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

Przykłady rzeczywistych błędów z powodu braku wiedzy na temat niuansów tematu.


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.