Swiftyn LogoSwiftyn
LearnInterview PrepRoadmapsArchitect Profile
Swift LanguageSwiftUIUIKitiOS ConceptsmacOS

Swift Language Topics

Introduction to SwiftVariables and ConstantsData TypesType InferenceOperatorsStrings and CharactersBooleansTuplesIf Else StatementsSwitch StatementsGuard StatementsLoopsBreak and ContinueArraysDictionariesSetsCollection OperationsFunctionsFunction ParametersReturn TypesInout ParametersVariadic ParametersClosuresTrailing ClosuresEscaping ClosuresAuto ClosuresCapture ListsOptionalsOptional BindingNil CoalescingOptional ChainingImplicitly Unwrapped OptionalsStructuresClassesPropertiesComputed PropertiesProperty ObserversMethodsInitializationDeinitializationInheritancePolymorphismEncapsulationAccess ControlStatic vs Class MethodsProtocolsProtocol ExtensionsProtocol CompositionAssociated TypesExtensionsGenericsGeneric ConstraintsOpaque TypesExistential TypesType CastingAny and AnyObjectNested TypesSubscriptsKeyPathsThrowing FunctionsDo Try CatchCustom ErrorsResult TypeARCStrong ReferencesWeak ReferencesUnowned ReferencesRetain CyclesMemory LeaksCopy on Write
Browse Swift Language Topics
Introduction to SwiftVariables and ConstantsData TypesType InferenceOperatorsStrings and CharactersBooleansTuplesIf Else StatementsSwitch StatementsGuard StatementsLoopsBreak and ContinueArraysDictionariesSetsCollection OperationsFunctionsFunction ParametersReturn TypesInout ParametersVariadic ParametersClosuresTrailing ClosuresEscaping ClosuresAuto ClosuresCapture ListsOptionalsOptional BindingNil CoalescingOptional ChainingImplicitly Unwrapped OptionalsStructuresClassesPropertiesComputed PropertiesProperty ObserversMethodsInitializationDeinitializationInheritancePolymorphismEncapsulationAccess ControlStatic vs Class MethodsProtocolsProtocol ExtensionsProtocol CompositionAssociated TypesExtensionsGenericsGeneric ConstraintsOpaque TypesExistential TypesType CastingAny and AnyObjectNested TypesSubscriptsKeyPathsThrowing FunctionsDo Try CatchCustom ErrorsResult TypeARCStrong ReferencesWeak ReferencesUnowned ReferencesRetain CyclesMemory LeaksCopy on Write
Swiftyn Logo

Swiftyn

The go-to platform for Apple developers. Swift, SwiftUI, and beyond.

Questions? Email us at support@swe180.com

Categories

  • SwiftUI
  • Swift Language
  • Xcode
  • visionOS

Our Products

  • SWE180
  • One Percent Engineer

Resources

  • About
  • RSS Feed
  • Apple Developer

© 2026 Swiftyn. All rights reserved.

Privacy PolicyTerms of Service

Swiftyn is the premier learning platform and developer resource for mastering the Apple ecosystem. Whether you are an aspiring iOS developer looking to learn Swift 6, a macOS engineer diving into advanced system architecture, or an XR pioneer building the future with visionOS, our beautifully crafted tutorials, roadmaps, and interview prep guides have you covered. Built by Apple developers, for Apple developers.

Swift Language10 min read

Mastering Swift Unowned References to Prevent Retain Cycles

Swift's automatic reference counting (ARC) simplifies memory management, but strong reference cycles can still lead to memory leaks. Unowned references provide a powerful mechanism to break these cycles when two objects have interdependent lifetimes but one will never be nil. Understanding their correct application is crucial for robust and efficient Swift code.

Introduction to ARC and Retain Cycles

Automatic Reference Counting (ARC) is a core feature of Swift that manages memory by tracking and counting how many 'strong' references currently point to an instance of a class. When an instance has zero strong references, ARC deallocates it, freeing up its memory.

However, ARC can't always resolve memory management issues on its own. A common problem is the 'strong reference cycle' or 'retain cycle.' This occurs when two instances hold strong references to each other, preventing either from being deallocated even when they are no longer needed by the rest of the application. This leads to a memory leak, as the memory consumed by these objects is never reclaimed. Understanding retain cycles is the first step towards preventing them effectively.

Consider a scenario where a Person class has a strong reference to a Residence class, and the Residence class also has a strong reference back to the Person. If you create instances of both and link them, they will keep each other alive indefinitely, even if all other references to them are set to nil.

Swift provides two primary ways to resolve strong reference cycles when dealing with class instances: weak references and unowned references. Both allow one object to refer to another without increasing its strong reference count, thereby breaking the cycle. The choice between weak and unowned depends on the relationship and lifetime of the objects involved, which we'll explore in detail.

swift
class Person {
    let name: String
    var residence: Residence?

    init(name: String) {
        self.name = name
        print("Person \(name) is being initialized")
    }

    deinit {
        print("Person \(name) is being deallocated")
    }
}

class Residence {
    let address: String
    var owner: Person?

    init(address: String) {
        self.address = address
        print("Residence \(address) is being initialized")
    }

    deinit {
        print("Residence \(address) is being deallocated")
    }
}

var john: Person? = Person(name: "John Appleseed")
var apt: Residence? = Residence(address: "123 Main St")

john?.residence = apt
apt?.owner = john

// Setting references to nil attempts to deallocate objects.
// Without weak/unowned, this won't call deinit due to a retain cycle.
john = nil
apt = nil
// Output will show only 'initialized' messages, no 'deallocated' messages.

What are Unowned References?

unowned references are a type of non-strong reference that you use when the other instance has the same or a longer lifetime. This means that an unowned reference always expects to be able to refer to an existing instance. You declare an unowned reference by placing the unowned keyword before the type annotation of a property or variable.

The key distinction from weak references is that an unowned reference is designed for situations where one object always has a strong reference to another, and the other object will never outlive the first. If you try to access an unowned reference after its referred instance has been deallocated, your app will crash at runtime. This behavior highlights a critical design assumption: the unowned reference is guaranteed to point to a valid instance as long as the referencing object itself exists.

When should you use unowned? Typically, when two properties must both be non-nil, and one property cannot exist without the other. For instance, in a Customer and CreditCard relationship, a customer might have a credit card, and a credit card will always be associated with a customer. If the customer is deallocated, the credit card should also be deallocated (or at least no longer be reachable by strong reference from the customer). In this case, neither should be nil upon initialization. This implies a specific ownership model: the customer 'owns' the credit card, but the credit card's reference back to the customer is unowned because the customer's lifetime is guaranteed to be equal to or longer than the credit card's.

unowned references are particularly useful for delegate patterns where the delegate (the unowned reference) is expected to live at least as long as the delegating object, or for parent-child relationships where the child's existence directly depends on the parent.

swift
import Foundation

class CreditCard {
    let number: UInt64
    unowned let customer: Customer // An unowned reference back to the customer

    init(number: UInt64, customer: Customer) {
        self.number = number
        self.customer = customer
        print("CreditCard #\(number) is being initialized")
    }

    deinit {
        print("CreditCard #\(number) is being deallocated")
    }
}

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

    init(name: String) {
        self.name = name
        print("Customer \(name) is being initialized")
    }

    deinit {
        print("Customer \(name) is being deallocated")
    }
}

var john: Customer? = Customer(name: "John Appleseed")
john?.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)

// Now, setting john to nil will deallocate both objects correctly
john = nil
// Expected Output:
// Customer John Appleseed is being initialized
// CreditCard #1234567890123456 is being initialized
// CreditCard #1234567890123456 is being deallocated
// Customer John Appleseed is being deallocated

Unowned Optional References (Introduced in Swift 5.0)

Before Swift 5.0, unowned references could not be optional. This meant that if you declared an unowned property, it had to be initialized with a non-nil value and could never become nil throughout its lifetime. This constraint could sometimes make it tricky to model certain relationships, especially during initialization where properties might temporarily be nil.

Swift 5.0 introduced unowned optional references (declared as unowned var subject: SomeClass?). This feature addresses scenarios where an unowned reference might temporarily be nil but still must always point to a valid instance when it's non-nil. This might seem contradictory to the primary use case of unowned, but it's useful in specific advanced scenarios, such as when dealing with mutually dependent optional properties that are set up in a two-phase initialization.

However, it's crucial to understand that even with unowned optional, the fundamental guarantee of unowned remains: if the reference is not nil and you attempt to access the referred instance, it must still be alive. If the instance has been deallocated and you access a non-nil unowned optional reference to it, it will still result in a runtime crash. Therefore, unowned optional adds flexibility during initialization but doesn't relax the lifetime assertion for non-nil access.

For most standard strong reference cycle breaking, the non-optional unowned or weak (for truly optional, potentially nil cases) is usually sufficient and clearer.

Compatibility Note: Unowned optional references are available from Swift 5.0 and later (iOS 12.0+, macOS 10.14+).

Unowned References and Closures

Retain cycles aren't limited to class properties; they can also occur with closures. A strong reference cycle can form if a closure captures self (or another class instance) strongly, and that same instance also holds a strong reference to the closure. This is a very common scenario in asynchronous programming or event handling.

To break these cycles in closures, you use a capture list. Inside a capture list, you can specify unowned self (or unowned SomeInstance) to create an unowned reference to the captured instance. This ensures that the closure does not keep the instance alive strongly.

When unowned self is used, it asserts that self will always be available and not nil for the entire lifetime of the closure's execution. If self gets deallocated before the closure is executed and you attempt to access self within the closure, your application will crash. This makes unowned self suitable for situations where the closure and the instance it captures have the same lifetime, or where the instance outlives the closure (e.g., self owns the closure, and the closure expects self to be there).

Conversely, weak self should be used when self might be deallocated before the closure finishes executing. With weak self, you capture self as an optional, allowing you to gracefully handle its potential absence (e.g., guard let strongSelf = self else { return }).

Choosing between weak and unowned in capture lists follows the same principles as choosing them for properties: unowned if you're sure the captured instance will never be nil when the closure executes; weak if it might be nil.

swift
import Foundation

class ConnectionManager {
    var connectionState: String = "Disconnected"
    var reconnectHandler: (() -> Void)?

    init() {
        print("ConnectionManager initialized")
        // Simulate an asynchronous operation that sets up a reconnect handler
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
            // Using unowned self because the reconnectHandler is owned by ConnectionManager
            // and implies ConnectionManager will exist when this closure might be called.
            // If the ConnectionManager *could* be deallocated before the handler is called, 
            // weak self would be safer.
            self.reconnectHandler = { [unowned self] in
                self.connectionState = "Reconnecting..."
                print("Connection state: \(self.connectionState)")
                // You could further define what happens after reconnecting
            }
        }
    }

    func simulateDisconnection() {
        connectionState = "Disconnected"
        print("Connection state: \(connectionState)")
        reconnectHandler?()
    }

    deinit {
        print("ConnectionManager deallocated")
    }
}

var manager: ConnectionManager? = ConnectionManager()
manager?.simulateDisconnection()
manager = nil
// Expected Output demonstrates correct deallocation due to unowned capture list:
// ConnectionManager initialized
// Connection state: Disconnected
// Connection state: Reconnecting...
// ConnectionManager deallocated

When to Choose unowned vs. weak

The decision between unowned and weak is crucial for correct memory management in Swift. Both break strong reference cycles, but they do so under different assumptions about object lifetimes.

Use unowned when:

  1. Lifetimes are interdependent and non-optional: The referenced instance has the same lifetime as, or a longer lifetime than, the referencing instance. Crucially, the unowned reference is always expected to point to a valid instance. If the unowned reference is ever accessed after its instance has been deallocated, it will cause a runtime crash.
  2. No nilability: The relationship dictates that the unowned property will never be nil (except possibly during very specific initialization phases for unowned optional in Swift 5+).
  3. Example: A CreditCard always belongs to a Customer. If the CreditCard refers to its Customer, it can be unowned because the Customer must exist as long as the CreditCard exists.

Use weak when:

  1. Lifetimes are independent or shorter: The referenced instance has a shorter lifetime than, or an independent lifetime from, the referencing instance. The weak reference might become nil at any point.
  2. Nilability allowed: The property must be an optional (var object: SomeClass?) because it could become nil (e.g., when the referenced object is deallocated). weak references are automatically set to nil when the object they refer to is deallocated.
  3. Example: A Delegate for a ViewController. The ViewController (delegator) might exist, but its Delegate (e.g., a presenter) might be released or replaced independently. The ViewController should hold a weak reference to its Delegate.

Think of it this way: unowned is a 'guaranteed existing' non-owner reference, while weak is a 'potentially non-existing' non-owner reference. Misusing unowned (when weak was appropriate) can lead to hard-to-debug crashes, so err on the side of caution with weak if you're not absolutely certain about lifetimes.

Ignoring Retain Cycles

Becoming a stronger iOS Engineer

THE MYTH or PROBLEM: Ignoring Retain Cycles

Many developers initially rely solely on ARC, overlooking the potential for strong reference cycles. This leads to insidious memory leaks, where objects are kept alive indefinitely, consuming memory even when no longer needed. The app might appear to work correctly, but memory usage steadily climbs, eventually leading to performance degradation or crashes. The core issue is when two class instances hold strong references to each other, preventing ARC from deallocating either.

swift
var objA: A? = A()
var objB: B? = B()
objA?.b = objB
objB?.a = objA // Strong reference cycle formed here
objA = nil
objB = nil
// Neither A nor B's deinit is called.

WHAT HAPPENS INTERNALLY? Reference Counting

ARC maintains a reference count for each class instance. When a strong reference is made, the count increments; when a strong reference is removed, the count decrements. An instance is deallocated when its strong reference count drops to zero. `unowned` references (and `weak` references) do not affect this strong reference count, allowing other strong references to correctly determine the instance's lifetime.

Memory Instance
Strong References
Unowned References
Weak References
1

1. Instance Initialization

A new class instance is created, its strong reference count is 1 (e.g., by the variable holding it).

2

2. Reference Assignment (Strong)

Assigning to another strong variable increments the count. If A holds B, and B holds A, both counts are 1 initially, then become 2.

3

3. Reference Assignment (Unowned)

Assigning to an `unowned` variable does NOT increment the strong reference count.

4

4. Deferencing (Strong)

When a strong reference is set to `nil` or goes out of scope, the count decrements.

5

5. Deallocation Blocked

If A points to B, and B points to A (both strong), their counts are 1 even after external references are nilled. Neither reaches 0, so deallocation is blocked.

Visualized execution hierarchy.

Powerful Guarantees

Lifetime Expectation

An `unowned` reference guarantees that the referenced object will live at least as long as the `unowned` referring object. Accessing a deallocated instance via `unowned` causes a runtime crash.

No Nilability

Typically, `unowned` references are non-optional. If they become `nil` (only possible with `unowned optional` Swift 5+), the accessing code *must* ensure the instance is still alive.

Prevents Strong Cycles

Does not increase the strong reference count of the referenced object, effectively breaking strong reference cycles.

REAL PRODUCTION EXAMPLE: Delegate Pattern Memory Leaks

A common mistake in production apps involves a `ViewController` setting itself as a delegate for a large helper object (e.g., a `LocationManager`, `APIClient`). If the `LocationManager` holds a strong reference to its `delegate` (the `ViewController`), and the `ViewController` implicitly or explicitly holds a strong reference to the `LocationManager`, a retain cycle occurs. The `ViewController` (and its view hierarchy) never gets deallocated, leading to significant memory leaks, especially with frequent navigation.

Impact / Results
Increased memory footprint over time
Gradual app slowdown
Potential for out-of-memory crashes
Stale UI updates from deallocated VCs
THE FIX: Declare Delegates as Unowned or Weak
swift
protocol LocationManagerDelegate: AnyObject { /* ... */ }

class LocationManager {
    // Use 'weak' primarily for delegates, as the delegate's lifetime
    // can be shorter than the delegator (LocationManager).
    // 'unowned' could be used if the LocationManager is always owned by its delegate.
    weak var delegate: LocationManagerDelegate?
    // Or with unowned, if `delegate` is guaranteed to exist as long as `LocationManager`
    // unowned let guaranteedDelegate: LocationManagerDelegate

    // ... (rest of LocationManager implementation)
}

class MyViewController: UIViewController, LocationManagerDelegate {
    let locationManager = LocationManager()

    override func viewDidLoad() {
        super.viewDidLoad()
        locationManager.delegate = self // MyViewController is the delegate
    }
    
    // ... (rest of MyViewController implementation)
}

INTERVIEW PERSPECTIVE

Common Question

“Explain a scenario where you would *prefer* using `unowned` over `weak`, and why.”

Strong Answer

A strong answer would describe a scenario like a `CreditCard` object having an `unowned` reference back to its `Customer`. The `Customer` 'owns' the `CreditCard` (strong reference). The `CreditCard` *must* always have a `Customer` and cannot exist without one; if the `Customer` is deallocated, the `CreditCard` should also be deallocated. Since the `Customer`'s lifetime is guaranteed to be equal to or longer than the `CreditCard`'s, and the `CreditCard`'s owner reference is never `nil` (once set), `unowned` is appropriate. It clearly states this strong dependency and provides a performance edge by not being optional, while `weak` would imply potential nilability which isn't the case here, making the intent less clear and adding unnecessary optional overhead.

Interviewers Expect you to understand:
  • Clear explanation of lifetime assumptions
  • Correct use case (e.g., parent-child where child implicitly needs parent)
  • Distinction from `weak` and why it's chosen
  • Understanding of runtime crash risk
KEY TAKEAWAY

Use `unowned` when you are absolutely certain that the referenced instance will *always* be alive whenever the `unowned` reference is accessed. It's for objects with interdependent lifetimes where one cannot exist without the other, and the 'owner' has an equal or longer lifetime than the 'owned' (non-owning) reference. Misuse leads to crashes.

Common Interview Questions

What is the primary difference between weak and unowned references?

The primary difference lies in their nilability and expectations about object lifetime. A `weak` reference is always an optional (`var SomeType?`) and automatically becomes `nil` when the referenced instance is deallocated, preventing crashes. An `unowned` reference is typically non-optional and assumes the referenced instance will *always* be alive during its own lifespan. Accessing an `unowned` reference after its instance has been deallocated will cause a runtime crash.

When should I use `unowned` in a closure's capture list?

Use `[unowned self]` in a closure's capture list when the closure and the instance it captures (`self`) are expected to have the same lifetime, or when `self` is guaranteed to outlive the closure. This is common when `self` owns the closure, and the closure implicitly relies on `self`'s continued existence. If `self` could be deallocated before the closure completes, `[weak self]` is safer.

Can `unowned` references be optional?

Yes, as of Swift 5.0, `unowned` references can be optional (`unowned var myObject: MyClass?`). This allows for more flexible initialization pathways where an `unowned` reference might temporarily be `nil`. However, the fundamental guarantee of `unowned` still applies: if the `unowned optional` reference is *not nil* and you access it, the referenced instance must be alive, otherwise it will crash.

What happens if I try to access a deallocated instance through an `unowned` reference?

If you try to access an instance through an `unowned` reference after that instance has been deallocated, your application will encounter a runtime error and crash. This is because `unowned` implicitly unwraps a value that is expected to always be present, and it does not perform a nil-check.

Are there any performance implications to using `unowned` vs. `weak`?

In practice, the performance difference between `unowned` and `weak` references is negligible for most applications. `weak` references involve slightly more overhead because they must be optional and ARC needs to nullify them upon deallocation. `unowned` references have a tiny bit less overhead because they don't need to be optional or cleared. However, this difference is almost never a deciding factor; correctness concerning lifetime management should always be the priority.

#Swift#Memory Management#ARC#Unowned#Retain Cycle#iOS