Zamknięcia (closures) w Swift to samodzielne bloki kodu, które mogą przechwytywać i przechowywać odniesienia do zmiennych i stałych z otaczającego kontekstu. Umożliwiają organizację callbacków, asynchroniczne przetwarzanie oraz przechowywanie wykonania kodu jako wartości zmiennych.
var counter = 0 let incrementer: () -> Void = { counter += 1 } incrementer() print(counter) // 1
[weak self], [unowned self]).self wewnątrz zamknięcia.Czym różni się strong-capture od użycia
[weak self]lub[unowned self]w liście przechwytywania zamknięcia? Jakie to rodzi konsekwencje?
Odpowiedź:
Jeśli nie wskażesz [weak self] lub [unowned self], zamknięcie domyślnie przechwyci self przez mocne odniesienie, co doprowadzi do cyklu zatrzymywania, jeśli zamknięcie i self będą odnosić się do siebie nawzajem. [weak self] przechwytuje self przez słabe odniesienie (opcjonalne), [unowned self] — przez niekontrolowane odniesienie (nieopcjonalne). Niewłaściwy wybór lub brak listy przechwytywania prowadzi do wycieków pamięci lub awarii.
class Test { var closure: (() -> Void)? func setup() { closure = { [weak self] in self?.doWork() } } func doWork() { ... } }
Historia
Programista nie użył listy przechwytywania w zamknięciu w UIViewController, który uruchamiał asynchroniczne ładowanie danych. W rezultacie controller i jego widok nie były zwalniane, co prowadziło do wycieku pamięci po zamknięciu (dismiss). Analiza infrastruktury wykazała setki "utknętych" w pamięci kontrolerów.
Historia
Zamknięcie przechwyciło self za pomocą [unowned self], ale cykl życia self zakończył się wcześniej niż zamknięcia. Powstała awaria przy próbie dostępu do już zwolnionego obiektu — aplikacja się zawieszała przy próbie pracy z self wewnątrz zamknięcia.
Historia
W projekcie wewnątrz zagnieżdżonego zamknięcia przechwycono dwie zmienne: jedną przez strong, drugą przez weak. Okazało się, że weak-odniesienie było zerowane zbyt wcześnie, a dane potrzebne do działania zamknięcia zostały utracone — błąd ujawnił się tylko w teście obciążeniowym w środowisku wielowątkowym.