Swift Strong, Weak, and Unowned References Explained

Swift uses Automatic Reference Counting (ARC) to manage the lifetime of class instances, and reference strength determines whether an object stays alive or can be released. Understanding strong, weak, and unowned references helps you avoid retain cycles, memory leaks, and crashes in everyday Swift code.

Quick answer: A strong reference keeps an object alive, a weak reference does not keep it alive and becomes nil automatically, and an unowned reference also does not keep it alive but assumes the object will always exist when used.

Difficulty: Intermediate

Helpful to know first: You'll understand this better if you know Swift classes, optionals, and how variables store references to objects.

1. What Are Strong, Weak, and Unowned References?

In Swift, a class instance is usually held by one or more references. The reference type determines how ARC treats that relationship.

These reference kinds matter only for class instances, because value types like struct and enum are copied rather than reference-counted.

2. Why Strong, Weak, and Unowned References Matter

Without careful reference management, two objects can keep each other alive forever. That creates a retain cycle, which means ARC cannot free the memory even when the objects are no longer needed.

These reference types help you model ownership correctly:

This distinction is especially important in parent-child relationships, delegates, closures, and object graphs with back-references.

3. Basic Syntax or Core Idea

Strong references

By default, Swift class references are strong. You usually do not write anything special to make a reference strong.

class Person {
    let name: String

    init(name: String) {
        self.name = name
    }
}
var person: Person? = Person(name: "Ava")

The variable person holds a strong reference to the instance. If you set it to nil and no other strong references exist, ARC can free the object.

Weak references

A weak reference is declared with the weak keyword and must be optional.

class Employee {
    weak var manager: Manager?
}

Because weak references can become nil at any time, you must safely unwrap them before use.

Unowned references

An unowned reference is declared with the unowned keyword.

class CreditCard {
    unowned let owner: Customer

    init(owner: Customer) {
        self.owner = owner
    }
}

An unowned reference is not optional, but you must be certain it is never accessed after the referenced instance has been deallocated.

4. Step-by-Step Examples

Example 1: A simple strong reference

This example shows the default ownership behavior. The object stays alive as long as the strong reference exists.

class Book {
    let title: String

    init(title: String) {
        self.title = title
        print("Book initialized")
    }

    deinit {
        print("Book deinitialized")
    }
}

var favoriteBook: Book? = Book(title: "The Swift Way")
favoriteBook = nil

When favoriteBook becomes nil, ARC releases the book because no strong references remain.

Example 2: Weak delegate pattern

Delegates are commonly held weakly so the delegate object can be released independently.

protocol DownloadManagerDelegate: AnyObject {
    func downloadManagerDidFinish()
}

class DownloadManager {
    weak var delegate: DownloadManagerDelegate?

    func finishDownload() {
        delegate?.downloadManagerDidFinish()
    }
}

Because the manager does not own its delegate, the delegate can be deallocated without creating a retain cycle.

Example 3: Parent-child relationship with unowned back-reference

When a child cannot exist meaningfully without its parent, unowned can be a good fit.

class Customer {
    let name: String
    var card: CreditCard?

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

class CreditCard {
    unowned let owner: Customer

    init(owner: Customer) {
        self.owner = owner
    }
}

The card can read its owner without increasing the owner's reference count, and the relationship stays safe as long as the customer outlives the card.

Example 4: Closure capture list to avoid a cycle

Closures also capture references strongly by default. Use a capture list to change that behavior.

class TimerOwner {
    var onTick: (() -> Void)?

    func start() {
        onTick = { [weak self] in
            self?.handleTick()
        }
    }

    func handleTick() {
        print("Tick")
    }
}

The capture list prevents the closure from strongly retaining self, which avoids a retain cycle if the closure is stored on the same object.

5. Practical Use Cases

6. Common Mistakes

Mistake 1: Creating a retain cycle with two strong references

Two classes can keep each other alive forever if both sides hold strong references. This is one of the most common ARC problems in Swift.

Problem: Neither object can be released because each one increases the other's reference count.

class Person {
    var apartment: Apartment?
}

class Apartment {
    var tenant: Person?
}

Fix: Make the back-reference weak when the apartment should not own the tenant.

class Person {
    var apartment: Apartment?
}

class Apartment {
    weak var tenant: Person?
}

The corrected version works because the apartment no longer keeps the person alive forever.

Mistake 2: Declaring a weak property as non-optional

A weak reference can become nil automatically, so Swift requires it to be optional.

Problem: Swift reports that weak properties must be declared optional because the referenced object may disappear at any time.

class Owner {}

class Widget {
    weak var owner: Owner
}

Fix: Add the optional marker to the weak reference.

class Owner {}

class Widget {
    weak var owner: Owner?
}

The corrected version compiles because Swift can set the property to nil when the owner goes away.

Mistake 3: Using unowned when the object can disappear first

Unowned is not a safer weak reference. It is a promise that the reference will always be valid when used.

Problem: If the referenced instance is deallocated first, accessing an unowned reference causes a runtime crash with a message similar to Attempted to read an unowned reference but object was already deallocated.

class Profile {}

class SettingsScreen {
    unowned var profile: Profile

    init(profile: Profile) {
        self.profile = profile
    }
}

Fix: Use weak when the object may disappear before the reference is used.

class Profile {}

class SettingsScreen {
    weak var profile: Profile?

    init(profile: Profile) {
        self.profile = profile
    }
}

The corrected version avoids a crash by allowing the reference to become nil safely.

7. Best Practices

Prefer strong references by default

Strong references are the normal ownership model in Swift. Use them unless you have a specific reason not to.

class Cache {
    var items: [String] = []
}

This is better than trying to make everything weak, because weak references do not express ownership and can vanish unexpectedly.

Use weak for back-references and delegates

If one object points back to another but should not keep it alive, weak is usually the right choice.

protocol PlayerDelegate: AnyObject {
    func playerDidFinish()
}

class Player {
    weak var delegate: PlayerDelegate?
}

This keeps ownership simple and makes object lifetimes easier to reason about.

Use unowned only when the lifetime relationship is guaranteed

Unowned can be appropriate, but only when the referenced object must outlive the reference.

class Country {
    let name: String
    init(name: String) { self.name = name }
}

class City {
    unowned let country: Country

    init(country: Country) {
        self.country = country
    }
}

If that guarantee is not absolute, choose weak instead.

8. Limitations and Edge Cases

9. Practical Mini Project

Here is a small, complete example that models a study group. The group owns its members strongly, but each member only keeps a weak reference to the group so there is no retain cycle.

final class StudyGroup {
    let name: String
    private var members: [Student] = []

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

    func add(student: Student) {
        members.append(student)
        student.join(group: self)
    }

    func listMembers() {
        print("Group: \(name)")
        for member in members {
            print("- \(member.name)")
        }
    }
}

final class Student {
    let name: String
    weak var group: StudyGroup?

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

    func join(group: StudyGroup) {
        self.group = group
    }

    func showMembership() {
        if let group {
            print("\(name) belongs to \(group.name)")
        } else {
            print("\(name) is not in a group")
        }
    }
}

do {
    let group = StudyGroup(name: "Swift Basics")
    let student = Student(name: "Mina")

    group.add(student: student)
    group.listMembers()
    student.showMembership()
}

This mini project shows a practical ownership model: the group owns its members, while members can refer back without preventing the group from being released.

10. Key Points

11. Practice Exercise

Create a simple model of a Teacher and a Classroom:

Expected output: The program should print the classroom name while the classroom exists, and then report that the teacher has no classroom after the classroom is released.

Hint: Make the back-reference optional and use if let before reading it.

final class Classroom {
    let name: String
    let teacher: Teacher

    init(name: String, teacher: Teacher) {
        self.name = name
        self.teacher = teacher
        teacher.assign(classroom: self)
    }
}

final class Teacher {
    let name: String
    weak var classroom: Classroom?

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

    func assign(classroom: Classroom) {
        self.classroom = classroom
    }

    func printStatus() {
        if let classroom {
            print("\(name) teaches in \(classroom.name)")
        } else {
            print("\(name) has no classroom")
        }
    }
}

do {
    var room: Classroom? = Classroom(name: "Room 201", teacher: Teacher(name: "Jordan"))
    room?.teacher.printStatus()
    room = nil
}

This solution works because the teacher does not keep the classroom alive, so the classroom can be released normally.

12. Final Summary

Strong, weak, and unowned references are central to Swift memory management. Strong references own objects by default, weak references provide safe non-owning links that can disappear, and unowned references express a non-owning relationship with a guaranteed lifetime.

Most Swift code uses strong references normally and introduces weak references to break retain cycles, especially for delegates and closures. Unowned references are useful in tightly coupled object relationships, but they should be used carefully because they can crash if the lifetime assumption is wrong.

When in doubt, choose the safer option: strong for ownership, weak for optional back-references, and unowned only when you can prove the referenced object will still exist. If you want to go deeper, the next topic to study is retain cycles in closures and how capture lists control ownership.