Logo for tanaschita.com

Memory management in Combine

Understand how retain cycles appear and when they turn into memory leaks in Combine.

12 Jan 2026 · 5 min read

Combine allows us to model asynchronous workflows declaratively. Publishers emit values, subscribers react to them, and operators connect everything into a clear pipeline.

What's easy to overlook is that Combine also introduces a specific ownership model. If we don't understand who holds references to whom, objects may live longer than expected, or never be released at all.

Let's look at how memory management works in Combine.

Sponsorship logo
Architecture & Design Patterns for iOS
This book is a practical guide to essential architectural principles and design patterns for iOS development. It covers strategies for building maintainable apps with Swift and SwiftUI, including dependency injection, navigation, common patterns, and modularization.
LEARN MORE

The ownership model in Combine

When we subscribe to a publisher using operators like sink or assign, Combine creates:

  • a subscriber
  • an AnyCancellable, which represents the subscription's lifetime

A simple rule of thumb is:

As long as the cancellable exists, the subscription remains active.

How retain cycles appear in Combine

Let's look at the following example:

var cancellable: AnyCancellable?
func startTimer() {
cancellable = Timer.publish(every: 1, on: .main, in: .default)
.autoconnect()
.sink { _ in
self.tick()
}
}

This creates a retain cycle:

self → cancellable → subscriber → closure → self

An important thing to understand is that a retain cycle not always leads to a memory leak.

Why a retain cycle does not always become a memory leak

A retain cycle is a structural issue: two or more objects hold strong references to each other, forming a cycle. A memory leak is a behavioral problem: an object remains in memory longer than intended and can never be released.

These two concepts are related, but they are not the same. A retain cycle can cause a memory leak, but only if nothing ever breaks the cycle.

For example, if the publisher completes, Combine tears down the subscription, so the closure gets released. This is why retain cycles caused by short-lived subscriptions do not turn into memory leaks.

Examples of short-lived publishers are network requests, Just, Future, finite sequences. In these cases, the retain cycle exists only temporarily and resolves itself automatically.

Long-lived subscriptions are different

Some publishers never complete on their own:

With these publishers, the retain cycle remains in place until we explicitly break it.

If we never cancel the subscription and capture self strongly, the cycle becomes a real memory leak.

Cancellation breaks the cycle

Explicit cancellation has the same effect as completion.

When we cancel a subscription or release the AnyCancellable, Combine tears down the subscription and releases the subscriber and closure.

func stopTimer() {
cancellable = nil
}

With this in place the retain cycle still exists while the timer is running. Once cancelled, the cycle is resolved. This is an important distinction: the code does create a retain cycle, but it does not necessarily leak. Of course, we risk a memory leak, for example when forgetting to call stopTimer.

Using weak self

To avoid retain cycles entirely, we can capture self weakly:

.sink { [weak self] _ in
self?.tick()
}

This breaks the cycle proactively. When self is deallocated, the subscription is cancelled automatically. This approach is especially important for long-lived subscriptions where cancellation is not guaranteed.

In practice:

  • If we capture strongly, we must guarantee cancellation
  • If cancellation is uncertain, weak captures are safer

In practice, using [weak self] is often the safest default, especially when the lifetime of a subscription is unclear or may change over time.

Sponsorship logo
Preparing for a technical iOS job interview
Preparing for a technical iOS Job Interview with over 300 questions & answers. Covering Swift & Objective-C, SwiftUI & UIKit, Combine, HTTP Networking, iOS File System, Core Data, Concurrency with async/await, Security, Automated Testing, Dependency Management, AI & Machine Learning and more.
LEARN MORE
Sponsorship logo
Architecture & Design Patterns for iOS
This book is a practical guide to essential architectural principles and design patterns for iOS development. It covers strategies for building maintainable apps with Swift and SwiftUI, including dependency injection, navigation, common patterns, and modularization.
LEARN MORE
Sponsorship logo
Become a sponsor of tanaschita.com
By publishing an article on different iOS topics every week, tanaschita.com is constantly growing in the developer community and may provide a great audience for you as a sponsor.
CLICK TO LEARN MORE

Newsletter

Image of a reading marmot
Subscribe

Like to support my work?

Say hi

Related tags

Articles with related topics

combine

reactive programming

swift

A visual cheat sheet of Combine operators

A diagram-based reference for common Combine publisher operators.

26 Jan 2026 · 5 min read

Latest articles and tips

© 2026 tanaschita.com

Privacy policy

Impressum