programowanieiOS developer

Co to jest ARC w Swift? Jak uniknąć cykli zatrzymywania przy pracy z closures i delegatami?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

ARC (Automatic Reference Counting) — system automatycznego zarządzania pamięcią w Swift. Przy każdym nowym silnym wskaźniku do obiektu licznik zwiększa się; przy usunięciu — zmniejsza. Kiedy licznik wynosi zero — obiekt jest zwalniany.

Retain cycle (cykl zatrzymywania) — sytuacja, w której obiekty odwołują się do siebie silnymi referencjami i nigdy nie są zwalniane.

Sposoby uniknięcia:

  • Dla obiektów delegacyjnych użyj słabej referencji:
protocol SomeDelegate: AnyObject { } class Owner { weak var delegate: SomeDelegate? }
  • Dla zamknięć użyj [weak self] lub [unowned self]:
class Example { var closure: (() -> Void)? func setup() { closure = { [weak self] in self?.doSomething() } } func doSomething() { } }

Pytanie podchwytliwe.

Jaka jest różnica między [weak self] a [unowned self] w obrębie closure? Kiedy używać jakiego typu referencji?

Odpowiedź:

  • [weak self] tworzy opcjonalną referencję; self może być nil, więc zazwyczaj stosuje się bezpieczne wydobywanie self?.
  • [unowned self] tworzy nie-opcjonalną referencję i NIE zwiększa licznika; jeśli obiekt został już zwolniony i następuje odwołanie — dojdzie do awarii.
  • Wybór zależy od logiki biznesowej: jeśli zamknięcie gwarantowanie żyje nie dłużej niż self, można użyć [unowned self].

Przykład:

someClosure = { [weak self] in self?.doTask() } // lub (jeśli self 100% żyje dłużej niż closure): someClosure = { [unowned self] in doTask() }

Przykłady rzeczywistych błędów z powodu nieznajomości niuansów tematu.


Historia

W projekcie powstała sytuacja cyklu zatrzymywania między ViewController a jego closure, związanym z obsługą kliknięcia. ViewController odnosił się do closure, które przechwytywało self silną referencją. W rezultacie, kontroler i wszystkie jego dane nie były zwalniane po zamknięciu ekranu. Problem rozwiązano poprzez wprowadzenie [weak self] w closure.


Historia

Implementacja wzorca delegata między dwoma obiektami. Delegat został zadeklarowany jako silna (strong) właściwość. W rezultacie przy próbie usunięcia głównego obiektu z pamięci nie został on zwolniony, co doprowadziło do wycieku pamięci. Po przejściu delegata na weak problem zniknął.


Historia

Do animacji używano closure wewnątrz UIView. Closure przechwytywało self za pomocą [unowned self]. Gdy widoki były usuwane przed zakończeniem animacji, aplikacja ulegała awarii z powodu odwołania się do już zwolnionego self. Rozwiązanie — użycie [weak self] i obowiązkowe sprawdzenie, czy self nie jest nil wewnątrz closure.