programowanieProgramista Swift

Co to jest defer w Swift, do czego jest potrzebny i jakie pułapki pojawiają się przy jego użyciu?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

Historia pytania:

defer został wprowadzony w Swift w celu lepszego zarządzania zasobami i wykonywania operacji po zakończeniu bloku kodu, analogicznie do finally w innych językach. Taka konstrukcja pomaga wyraźnie pokazać etapy oczyszczania lub kończenia pracy z zasobami.

Problem:

Wielu początkujących uważa, że defer od razu wykonuje zagnieżdżony kod, nie zawsze prawidłowo rozumiejąc moment wywołania. Sytuacje, w których jest wiele bloków defer, mogą być trudne do zrozumienia, jeśli nie znasz zasady LIFO (last-in, first-out). Mogą wystąpić trudności z try/catch i wczesnym wyjściem.

Rozwiązanie:

defer wykonuje zagnieżdżony kod na samym końcu zakresu, przed wyjściem z aktualnego bloku funkcji, nawet jeśli wyjście z funkcji następuje wcześniej (przez return, throw, break itp.). Kilka defer wykonuje się w odwrotnej kolejności ich wystąpienia.

Przykład kodu:

func readFile() { print("Otwórz plik") defer { print("Zamknij plik") } print("Przeczytaj linię 1") if Bool.random() { print("Wczesny powrót!") return } print("Przeczytaj linię 2") } readFile() // Na konsoli zawsze będzie: Otwórz plik, ..., Zamknij plik (na końcu)

Kluczowe cechy:

  • defer zawsze wywołuje się przy wychodzeniu z zakresu funkcji/metody
  • kilka defer działa na zasadzie stosu (LIFO)
  • wywołuje się nawet przy wczesnym return lub błędach

Pytania pułapki.

Czy można używać defer poza funkcją?

Nie, defer jest dozwolony tylko wewnątrz funkcji, metod, initializers i deinitializers. Nie można go umieścić np. w globalnej przestrzeni pliku lub wewnątrz źródła kodu poza funkcjami.

Co się stanie z defer, jeśli w bloku wystąpi wyjątek (throw)?

defer i tak zostanie wykonany. To jedna z jego zalet — zasób jest gwarantowany, że zostanie zwolniony nawet w przypadku błędu (throw). Tak realizowany jest wzorzec bezpiecznego zwalniania zasobów.

W jakiej kolejności wykonywane są różne defer?

Wykonywane są w odwrotnej kolejności (LIFO): ten defer, który został zadeklarowany później, zostanie wykonany wcześniej.

Przykład kodu:

func test() { defer { print("Pierwszy") } defer { print("Drugi") } print("Wewnątrz") } test() // Wyświetli: "Wewnątrz", "Drugi", "Pierwszy"

Typowe błędy i antywzorce

  • Używanie defer do kodu, w którym mogą wystąpić własne return/throw, zamieszanie w kolejności wykonywania
  • Nadużywanie defer, komplikowanie czytelności funkcji z wieloma blokami
  • Próba użycia defer poza zakresem funkcji

Przykład z życia

Negatywny przypadek

Funkcja z wieloma, niejednoznacznie umiejscowionymi defer; zapomniana sondaż niektórych zasobów, powracamy z funkcji w różnych miejscach. Powoduje to wycieki zasobów i trudności w debugowaniu zachowania.

Zalety: Jednolitość kodu, "zebrane" działania oczyszczania w jednym miejscu.

Wady: Trudno śledzić, co się wykona i w jakiej kolejności; mogą wystąpić wycieki przy błędach w logice.

Pozytywny przypadek

Tworzenie jednego defer dla każdego ważnego etapu, umieszczonego w tym samym miejscu, gdzie zasób jest inicjowany, z komentarzami. Kontrola kodu przez zespół.

Zalety: Gwarantowane zwolnienie zasobów, jasne zrozumienie kolejności działań.

Wady: Wymaga dyscypliny i uwagi przy dodawaniu nowych zasobów i bloków oczyszczania.