Weak Reference — What It Is, Syntax, and Usage in Mobile Development

Author: IT Sectr Published: 2026-03-30 Reading time: 9 min

Weak Reference is a reference to an object that does not increase its retain count in ARC. According to Apple Swift Language Guide, 2026, weak references are declared with the weak keyword and are always optional. When the object is deallocated, all weak references to it are automatically set to nil, preventing dangling pointers and making weak references a safe mechanism for breaking retain cycles.

Key Takeaways

  • Weak Reference — a reference that does not affect the object’s retain count; becomes nil when the object is deallocated
  • Declaration — the weak keyword before var; type is always optional (?)
  • Usage — delegates, closures, parent-child relationships for breaking retain cycles
  • Safety — automatic setting to nil after object deallocation (zeroing weak)
  • Difference from unowned — weak becomes nil and is safe, unowned does not become nil and requires lifetime guarantees

What is Weak Reference?

Weak Reference is a non-owning reference to an object in ARC (Automatic Reference Counting). Unlike a strong reference, which increases the object’s retain count and guarantees its lifetime, a weak reference allows the object to be deallocated even if it is still being referenced. After deallocation, the weak reference is automatically set to nil — this is called zeroing weak.

Zeroing weak is a key feature of the Swift and Objective-C runtime. When an object’s reference count reaches zero and the object is deallocated, the runtime iterates through all weak references to this object (stored in a special weak table) and sets them to nil. This ensures that accessing freed memory (use-after-free) is impossible through weak references — any read returns nil.

According to Apple WWDC 2012 Session 406, zeroing weak references eliminated an entire class of crash bugs related to dangling pointers, which were common in manual memory management (MRR). In MRR, weak references only existed as __unsafe_unretained — they did not zero out, and accessing a deallocated object resulted in EXC_BAD_ACCESS.

Weak Syntax in Swift and Objective-C

Let’s look at the syntax for declaring weak references in both Apple ecosystem languages. Despite the shared runtime, the syntax differs, but the semantics are identical.

Swift

In Swift, weak references are declared with the weak keyword before var. The type must always be optional (Type?), since the reference can become nil at any time. Constants (let) cannot be weak — only variables.

swift
class ViewController: UIViewController {
    // weak properties: only var, only optional
    weak var delegate: ViewControllerDelegate?
    weak var parentView: UIView?

    weak var completionHandler: ((Bool) -> Void)?  // ⚠️ closures do not store weak
    // ⬆️ Error: weak can only be applied to class types, not closures
}

Important: weak is only applicable to class instances (class types), AnyObject, and protocols inherited from AnyObject. Struct, enum, and closures cannot be weak — they are value types and do not participate in ARC.

Objective-C

In Objective-C, weak properties are declared using the __weak attribute or the weak modifier in property declarations:

objective-c
// Objective-C: weak property
@interface MyViewController : UIViewController
@property (weak, nonatomic) id<MyDelegate> delegate;
@end

// Local weak variable
__weak MyObject *weakRef = someStrongObject;

The Objective-C runtime also provides zeroing weak, but additionally blocks the use of weak with C structures and some Core Foundation objects. For these, __unsafe_unretained is used — without zeroing.

When to Use Weak References

Weak references are not a universal solution, but a tool for specific scenarios. Using weak everywhere leads to unnecessary complexity and hurts readability. Let’s look at the correct usage scenarios.

Delegates (Delegate pattern)

Delegates — the primary scenario for weak. The owning object (e.g., UITableView) holds a strong reference to itself, while the delegate (UIViewController) should not own the table. Apple SDK guarantees that all delegates and dataSources are weak. For your own protocols, always use weak var delegate.

Parent-Child with Backreference

When a child object needs to reference its parent (e.g., ChildViewController accessing a coordinator), use a weak reference. The parent owns the child (strong), the child observes the parent (weak) — retain cycle is eliminated.

Asynchronous Closures

Capture list [weak self] — the standard way to avoid retain cycles in closures stored as class properties. If self may be deallocated before the closure completes, weak self is mandatory.

ScenarioWeakStrong
Delegate✅ Always weak❌ Retain cycle
Parent → Child❌ Not needed (parent should own)✅ Strong
Child → Parent✅ Weak❌ Retain cycle
Async callback✅ [weak self]❌ Retain cycle risk
Strong coupling (owned)❌ unowned✅ Strong

General rule: if object A owns B (A → B strong), then B → A should be weak or unowned. The direction of strong references should always be from owner to subordinate.

Weak vs Unowned: Comparison and Scenarios

Both weak and unowned do not increase the retain count, but differ in behavior after object deallocation. The choice between them is a matter of lifetime guarantees.

Differences

Weak: automatically becomes nil, type is always optional, requires unwrapping before use. Safe — accessing nil does not cause a crash.

Unowned: does not become nil, type is non-optional. If the object is deallocated, an unowned reference becomes a dangling pointer — accessing it causes a runtime crash. Unowned assumes the object lives at least as long as the referencing side.

When to Choose Weak

Choose weak if: the object may be deallocated at any time (delegate after screen dismissal), you do not control the object’s lifetime, or you are unsure about guarantees. Weak is the universal safe choice.

When to Choose Unowned

Choose unowned if: the object is guaranteed not to be deallocated before the referencing object (e.g., Customer → CreditCard, where the card does not exist without the customer). Unowned provides a non-optional API without unwrapping, which is more convenient in code.

swift
class Order {
    let id: Int
    var items: [Item] = []

    init(id: Int) { self.id = id }

    // Strong relationship: Order owns Item
    func addItem(name: String) {
        let item = Item(name: name, order: self)
        items.append(item)
    }
}

class Item {
    let name: String
    unowned let order: Order          // ✅ unowned — Item does not live without Order

    init(name: String, order: Order) {
        self.name = name
        self.order = order
    }
}

// Example with weak: delegate without lifetime guarantee
protocol NetworkServiceDelegate: AnyObject {
    func didReceiveResponse(data: Data)
}

class NetworkService {
    weak var delegate: NetworkServiceDelegate?  // ✅ weak — delegate may go away
}

In the example, Item uses unowned because an order item cannot exist without the order itself — the lifetime guarantee is ironclad. NetworkService uses weak because the delegate (e.g., ViewController) may be dismissed and deallocated at any time.

Weak Reference Limitations and Pitfalls

Weak references are a powerful tool, but they have limitations that are important to understand for correct usage in iOS development.

Weak Performance

Weak references are slower than strong: on each access, the runtime checks whether the object has been deallocated (lookup in the weak table). In the vast majority of scenarios, the difference is imperceptible, but in hot loops with millions of accesses, weak can become a bottleneck. For high-load scenarios, use strong and reorganize the architecture.

Weak is Not Applicable to Value Types

Struct, enum, tuple — value types that do not participate in ARC. Attempting to declare a weak struct results in a compilation error. To store a weak reference to a value type, use a wrapper in a class type or a closure.

Weak in Multithreading

Zeroing weak is thread-safe: if an object is deallocated on one thread, the weak reference is zeroed on all threads atomically. However, the window between reading a weak reference and dereferencing it can lead to a race condition — the object is deallocated between obtaining the weak reference and using it. Solution: strong capture of the weak reference into a local variable.

swift
// Race condition with weak in multithreading
func performAsync() {
    weak var weakSelf = self
    queue.async {
        // ⚠️ weakSelf may be nil between check and use
        if weakSelf != nil {
            weakSelf!.doSomething()  // CRASH if becomes nil
        }
    }
}

// ✅ Fix: strong capture during use
func performAsyncSafe() {
    queue.async { [weak self] in
        guard let strongSelf = self else { return }
        strongSelf.doSomething()  // strongSelf — local strong reference
    }
}

In the safe version, weak self is captured, then immediately unwrapped into a local strong variable strongSelf. If self is still alive, it will remain alive for the duration of the block. If not, guard triggers and the code does not execute. This idiom is the standard pattern for asynchronous closures in Swift.

UIView and Weak Outlets

IBOutlet in Interface Builder should be weak because the view hierarchy already holds a strong reference to the subview. Duplicating a strong reference in the controller does not create a retain cycle but is redundant. A weak reference to an outlet is Apple’s recommendation, although many developers use strong for code simplicity.

Frequently Asked Questions

Can a weak reference point to an object that hasn’t been created yet?

No, weak can only point to an existing object or nil. When creating a new object, you first obtain a strong reference (via an initializer), and only then can you assign a weak reference. A weak nil at the start is a normal state.

Why does weak only work with class types?

Weak is based on ARC, which only manages reference types (classes). Value types (struct, enum) are copied on assignment and do not have a retain count. For weak relationships with value types, use closures or wrappers in a class with a weak property.

How does weak affect performance in a loop?

Each access to a weak reference performs a lookup in the runtime table. In a loop with millions of iterations, this can be 2–5 times slower than a strong reference. For hot paths, copy weak into a local strong variable before the loop.

When can a weak reference unexpectedly become nil?

When all strong references to the object are lost — at the end of scope, when a property is reassigned, or when a screen is dismissed. In a multithreaded environment, this can happen between two lines of code. Always check weak references with guard let or if let.

How does weak differ from __weak in Objective-C?

Semantically identical: both provide zeroing weak. Differences: Swift requires an optional type and var, Objective-C uses a property modifier. Objective-C also supports __unsafe_unretained — a weak reference without zeroing (risk of dangling pointer).

Summary

  • Weak Reference — a non-owning reference that does not increase the retain count and is automatically zeroed on deallocation
  • Syntaxweak var + optional type; only class types and AnyObject protocols
  • Zeroing weak — runtime zeroes all weak references to a deallocated object, preventing dangling pointers
  • Scenarios — delegates, parent-child with backreference, asynchronous closures ([weak self])
  • Weak vs Unowned — weak becomes nil (safe), unowned does not become nil (crash risk, but non-optional)
  • Performance — weak is slower than strong due to lookup in the runtime table; for hot paths, copy to strong
  • Recommendation — if unsure about lifetime guarantees, choose weak

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also