This is the introduction to a multi-part series.

  1. Stack and Heap for Swift (and other) Programmers (you are here)
  2. The Virtual Address Space
  3. Introduction to the Stack
  4. Limitations of the Stack (coming soon)
  5. Introduction to the Heap
  6. Stack and Heap for Swift Programmers
  7. Stack and Heap for SwiftUI programmers

Modern programming languages like Swift insulate programmers from the drudgery of manual memory management. That’s a great thing, since erroneous memory management has been the leading cause of security vulnerabilities in software1.

Swift’s automatic memory management is a recent example of a trend that dates back to the beginning of computer programming: teaching the computer to do the boring (but essential!) jobs, so we can focus on writing the code that makes our programs unique.

At this point in programming history, the pile of abstraction layers on which our programs rest is deep. Each layer shields programmers from numerous details we’d otherwise have to sweat ourselves. Those details represent decades of hard-won knowledge, and as a result, our programs are quicker to write and safer to run.

Memory management abstractions are leaky

But the details of memory management are particularly prone to leak out of the layers in which they’re normally concealed.

Whenever we look at a crash log, or step through program code in a debugger, the call stack—a region of memory we normally don’t have to think about because it’s handled by the compiler—is unveiled.

Screenshot of Xcode stopped in the debugger at a crash due to an "index out of range" assertion failure. The text "Who's keeping track of all this?" has been edited into the screenshot, with an arrow indicating that "all this" is the stack trace in Xcode's debug navigator.

If we’re not familiar with the call stack, we may not know that it’s a limited resource which even simple programs can exhaust, causing a crash. It can also exhibit performance problems if we use it with large data values. How can we avoid these problems?

Similarly, when writing Swift code, we have to reason about whether we should model our data using value types or reference types. We may have learned that value type variables are allocated on the call stack, whereas reference type variables are allocated in a different region of memory called the heap. But just as the call stack is managed for us by the compiler, in Swift the heap is managed by Automatic Reference Counting, so we don’t have to know details of the heap to write Swift code. How should we determine when to use value types or reference types?

Consider this SwiftUI code.

struct RandomNumberGeneratorView: View {
    @ObservedObject var viewModel: RandomNumberGeneratorViewModel

    var body: some View {
        VStack {
            Text("Random number generator")
            Text(String(viewModel.number))
                .font(.largeTitle)

            Button {
                viewModel.generateNewNumber()
            } label: {
                Text("Generate")
            }
        }
        .padding()
        .roundRectBackground()
    }

    init(viewModel: RandomNumberGeneratorViewModel) {
        _viewModel = ObservedObject(wrappedValue: RandomNumberGeneratorViewModel())
    }
}

It compiles2, and in certain cases appears to work fine. But in other cases it fails badly. Why? Knowing the differences between stack-allocated values and heap-allocated objects makes this easier to recognize.

Details worth studying

Let’s peel open the layers hiding the details of stack- and heap-based memory management. Learning (or remembering) these details builds intuition about how to design programs that handle data correctly and efficiently.

When writing SwiftUI apps in particular, it makes it easier to understand how and why to use view property wrappers.

But whatever languages we use, knowledge of the stack and heap is valuable, because they are key components of the foundation on which almost3 every structured programming language is built. Along the way we’ll have an opportunity to discuss other useful topics like reading assembly language.

Road map

We’ll examine the details of the stack and heap—and what they mean for our programs—in six parts.

First, in The Virtual Address Space, we’ll explore the landscape in which the stack and heap are situated, and why they are needed.

Then we’ll meet the stack in the unsurprisingly-named Introduction to the Stack.

Once acquainted with the stack, we’ll discover that it doesn’t solve all runtime memory management problems in Limitations of the Stack.

Don’t worry, we’ll learn in Introduction to the Heap how its features complement the stack’s, and vice-versa.

We can then apply our knowledge of the stack and heap to Swift and SwiftUI in

Stack and Heap for Swift programmers

and

Stack and Heap for SwiftUI programmers.

Who is this for?

I’m writing these posts for programmers who have some experience writing programs in Swift, or another modern compiled language, but who don’t know or would like to learn more about how those languages manage memory at runtime. I’m assuming you’ve learned about

Please help me improve these posts!

A lot of the material in these blog posts is new to me. I would love to hear from you if you have corrections or suggestions for improvement! Contact me on Cohost or Mastodon.

Let’s begin!

Join me in The Virtual Address Space.


  1. https://alexgaynor.net/2020/may/27/science-on-memory-unsafety-and-security/ 

  2. Not all the code necessary for it to compile is shown. 

  3. I don’t currently know of any exceptions, but there are a lot of languages I haven’t studied.