0
0
Swiftprogramming~7 mins

Closures causing retain cycles in Swift

Choose your learning style9 modes available
Introduction

Closures can keep objects alive by holding strong references to them, causing memory to not be freed. This is called a retain cycle.

When you have a closure inside a class that refers to self or its properties.
When you want to avoid memory leaks in apps with long-running closures.
When you use closures as callbacks or completion handlers that capture self.
When you want to understand why your app's memory usage grows unexpectedly.
When debugging why objects are not being deallocated.
Syntax
Swift
class MyClass {
    var closure: (() -> Void)?

    func setup() {
        closure = {
            print(self)
        }
    }
}

Closures capture variables from their surrounding context, including self.

If self holds a strong reference to the closure, and the closure captures self strongly, a retain cycle happens.

Examples
This code creates a retain cycle because closure captures self strongly, and self owns closure.
Swift
class MyClass {
    var closure: (() -> Void)?

    func setup() {
        closure = {
            print(self)
        }
    }
}
Using [weak self] breaks the retain cycle by capturing self weakly inside the closure.
Swift
class MyClass {
    var closure: (() -> Void)?

    func setup() {
        closure = { [weak self] in
            guard let self = self else { return }
            print(self)
        }
    }
}
[unowned self] also breaks the retain cycle but assumes self will not be nil when closure runs.
Swift
class MyClass {
    var closure: (() -> Void)?

    func setup() {
        closure = { [unowned self] in
            print(self)
        }
    }
}
Sample Program

This example shows how to use a weak reference in a closure to avoid a retain cycle. When person is set to nil, the object is freed properly.

Swift
class Person {
    let name: String
    var greeting: (() -> Void)?

    init(name: String) {
        self.name = name
    }

    func sayHello() {
        greeting = { [weak self] in
            guard let self = self else { return }
            print("Hello, my name is \(self.name)")
        }
    }
}

var person: Person? = Person(name: "Alex")
person?.sayHello()
person?.greeting?()
person = nil
print("Person is now nil")
OutputSuccess
Important Notes

Always use [weak self] or [unowned self] in closures that capture self to avoid retain cycles.

Use weak when self can become nil during closure lifetime.

Use unowned when you are sure self will not be nil when closure runs.

Summary

Closures capturing self strongly can cause retain cycles and memory leaks.

Use capture lists like [weak self] or [unowned self] to break retain cycles.

Understanding retain cycles helps keep your app memory efficient and bug-free.