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