programowanieProgramista Swift

Co to jest 'defer' w Swift? Jak to działa i do czego jest stosowane, jakie nietypowe scenariusze jego używania istnieją?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

Operator defer odkłada wykonanie kodu do momentu opuszczenia bieżącego zasięgu, zazwyczaj z funkcji. Przydatny do czyszczenia zasobów, zamykania plików i zwalniania pamięci — tak zwany zautomatyzowany cleanup.

Cechy:

  • Bloki defer wykonują się w odwrotnej kolejności ich zadeklarowania (LIFO).
  • Wywołanie następuje zawsze — zarówno przy normalnym wychodzeniu z funkcji, jak i przy wyrzuceniu błędu.
  • Pozwala uniknąć powielania kodu czyszczącego i wspiera czystą architekturę funkcji.

Przykład:

func processFile() { let file = openFile() defer { closeFile(file) } // Praca z plikiem // plik będzie zamknięty nawet w przypadku throw lub return }

Nietypowe zastosowanie: częściowa imitacja konstrukcji typu 'finally', grupowanie kodu do logowania i śledzenia czasów pracy.

Pytanie z podstępem.

Czy defer zostanie wykonany, jeśli aplikacja zakończy działanie w czasie wykonywania funkcji?

— Nie. Defer jest wykonywany tylko przy normalnym zakończeniu zasięgu (return, throw lub normalne wyjście). Jeśli aplikacja zakończona jest awaryjnie (na przykład z powodu sygnału SIGKILL lub fatalError), bloki defer nie zdążą się wykonać.

Przykład:

func foo() { defer { print("Czyszczenie") } fatalError("Awaria!") // defer się nie wykona }

Przykłady rzeczywistych błędów spowodowanych nieznajomością szczegółów tematu.


Historia

Funkcja, która pracowała z gniazdami, nie czyściła połączenia w przypadku throw. Dane pozostawały wisząc, połączenia nie były zwalniane. Po wprowadzeniu defer wszystko działało poprawnie: zasoby były zawsze zamykane.


Historia

Programista próbował polegać na defer do zwalniania globalnego stanu. Przy wystąpieniu fatalError zasób nie był zwalniany, co prowadziło do blokowania usług i konieczności restartu.


Historia

W funkcji zadeklarowano kilka defer, mając nadzieję, że zostaną wykonane w kolejności deklaracji. W wyniku tego zasoby były zamykane w niewłaściwej kolejności (na przykład najpierw zamknięto deskryptor pliku, a potem próbowano do niego odwołać). Rozwiązanie: pamiętać o kolejności LIFO wykonywania bloków defer.