3.3 · Cycles: delegates, closures, timers, Combine, tasks

ARC cannot free two objects that hold each other. Real apps create cycles in a handful of recurring shapes, and every one has the same cure: break one link.

Mental model. Ask of every long-lived thing (a timer, a subscription, a task, a stored closure): who keeps it alive, and what does it keep alive? If the answer is a loop back to you, you have a cycle.

The shape of a cycle

What it is, how to spot it, and the delegate case that started it all.

  1. What is a retain cycle, and how do you find and fix one? 3.3.1 · L1
    A retain cycle is a loop of strong references: A holds B and B holds A (or a longer ring, A to B to C to A). Every object in the loop has at least one strong…
  2. Why should a delegate property be weak, and why must the protocol be class-bound? 3.3.2 · L1
    A delegate is usually the owner of the object that calls it: a screen owns its data service, and the service reports back to the screen. If the service held…

Closures and timers

Stored closures and the run loop that holds your object.

  1. When does a closure create a cycle, and why doesn’t a lazy var closure always? 3.3.3 · L2
    A closure that uses self keeps self alive. That is harmless until self also holds the closure. A stored closure property such as var onTap: (() -> Void)? or a…
  2. Why does a Timer keep your object alive, and how do you stop it? 3.3.4 · L2
    A scheduled Timer is retained by the run loop until it is invalidated, and the timer retains whatever its callback captures. A repeating timer whose block uses…

Reactive and async code

Combine subscriptions and unstructured tasks.

  1. How do Combine subscriptions create cycles, and why is assign(to:on:) a trap? 3.3.5 · L3
    A common pattern stores subscriptions in the object they report to: publisher.sink { self.update($0) }.store(in: &cancellables). The chain is self to…
  2. Can a Task cause a retain cycle, and how do you avoid it? 3.3.6 · L3
    An unstructured Task { … } that uses self captures it strongly for as long as the task runs. For a short task that is fine: it finishes and releases. For a…

More in Memory, ARC & runtime