ProgrammingiOS Developer

What is ARC in Swift? How to avoid retain cycles when working with closures and delegates?

Pass interviews with Hintsage AI assistant

Answer.

ARC (Automatic Reference Counting) is the automatic memory management system in Swift. Each time a strong reference to an object is created, the counter increases; when it is removed, the counter decreases. When the count reaches zero, the object is deallocated.

Retain cycle is a situation when objects reference each other with strong references and are never deallocated.

Ways to avoid:

  • For delegate objects, use a weak reference:
protocol SomeDelegate: AnyObject { } class Owner { weak var delegate: SomeDelegate? }
  • For closures, use [weak self] or [unowned self]:
class Example { var closure: (() -> Void)? func setup() { closure = { [weak self] in self?.doSomething() } } func doSomething() { } }

Trick question.

What is the difference between [weak self] and [unowned self] inside a closure? Which type of references to use and when?

Answer:

  • [weak self] creates an optional reference; self can be nil, so usually safe unwrapping self? is used.
  • [unowned self] creates a non-optional reference and does NOT increase the counter; if the object has already been deallocated and access occurs, it will crash.
  • The choice depends on business logic: if the closure is guaranteed to outlive self, you can use [unowned self].

Example:

someClosure = { [weak self] in self?.doTask() } // or (if self is guaranteed to outlive the closure): someClosure = { [unowned self] in doTask() }

Examples of real errors due to ignorance of the subtleties of the topic.


Story

In the project, a retain cycle situation arose between the ViewController and its closure associated with handling taps. The ViewController held a reference to the closure, which captured self with a strong reference. As a result, the controller and all its data were not deallocated after the screen was closed. The problem was resolved by implementing [weak self] in the closure.


Story

Implementation of the delegate pattern between two objects. The delegate was declared as a strong property. Consequently, when attempting to delete the main object from memory, it was not deallocated, leading to a memory leak. After changing the delegate to weak, the issue disappeared.


Story

For animation, a closure was used inside UIView. The closure captured self using [unowned self]. When the view was removed before the animation completed, the application crashed due to access to already deallocated self. The solution was to use [weak self] and mandatory checking for non-nil self within the closure.