defer to specjalna konstrukcja w Swift, która pozwala na wykonanie określonego bloku kodu bezpośrednio przed wyjściem z zakresu (scope) funkcji, niezależnie od tego, czy wychodzimy z niej przez zwykły return, czy przez błąd (throw). Defer jest wygodny do użycia w celu zwolnienia zasobów, anulowania zmian lub finalizacji operacji.
Cechy działania:
Przykład:
func testDefer() { print("początek") defer { print("pierwszy defer") } defer { print("drugi defer") } print("koniec") } // Wydrukuje: // początek // koniec // drugi defer // pierwszy defer
Przypadki graniczne:
Czy wszystkie bloki defer w funkcji zostaną wykonane, jeśli wewnątrz niej wywołamy fatalError?
Odpowiedź: Nie, jeśli w funkcji wywołano fatalError (lub podobne niekontrolowane awarie), wszystkie opóźnione bloki przez defer nie zostaną wykonane. defer nie gwarantuje wywołania kodu w przypadkach awaryjnego zakończenia działania aplikacji.
Przykład:
func foo() { defer { print("Defer 1") } fatalError("Ups") defer { print("Defer 2") } } foo() // nic nie wydrukuje, nastąpi awaria
Historia
W projekcie używano defer do zamknięcia deskryptora pliku. W przypadku wystąpienia błędu używano fatalError zamiast poprawnego throw, co prowadziło do wycieku otwartego zasobu, ponieważ defer nie działał przy awarii.
Historia
W jednej funkcji było kilka defer, z których niektóre zależały od stanu zmiennych lokalnych. Programista oczekiwał, że zmienna będzie miała specyficzną wartość, ale ta wartość w defer zmieniła się przez inny fragment kodu, i podczas wykonywania defer użyto aktualnej, a nie starej wartości, co prowadziło do błędu anulowania transakcji nie z odpowiednim ID.
Historia
W zagnieżdżonym closure napisano blok defer, sądząc, że zostanie on wykonany przy wyjściu z zewnętrznej funkcji. W rezultacie ten defer został wykonany przy wyjściu z closure, a nie całej funkcji, i zasób został zwolniony zbyt wcześnie.