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.
- What is a retain cycle, and how do you find and fix one?
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… - Why should a delegate property be weak, and why must the protocol be class-bound?
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.
- When does a closure create a cycle, and why doesn’t a lazy var closure always?
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… - Why does a Timer keep your object alive, and how do you stop it?
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.
- How do Combine subscriptions create cycles, and why is assign(to:on:) a trap?
A common pattern stores subscriptions in the object they report to: publisher.sink { self.update($0) }.store(in: &cancellables). The chain is self to… - Can a Task cause a retain cycle, and how do you avoid it?
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
- 3.1 · ARC, autorelease pools, object lifetime
- 3.2 · strong, weak, unowned, unowned(unsafe)
- 3.4 · Object layout, side tables, tagged pointers
- 3.5 · The copy-on-write mechanism
- 3.6 · Retain traffic, parameter conventions, Unmanaged, bridging
- 3.7 · Leaks, abandoned memory, footprint, and the tools
- 3.8 · deinit rules, process exit, isolated deinit, noncopyable types and consume
- 3.9 · Weak collections, nested closures, structs holding references, weak let and escaped pointers
- 3.10 · Enum layout, existential boxing, tail allocation, slice retention, growth and the command-line tools