[weak self] in Swift

Janvi Arora
5 min readMar 16, 2023

--

In Swift, [weak self] is a capture list used in closures to avoid strong reference cycles, also known as retain cycles. A retain cycle occurs when two or more objects hold strong references to each other, which prevents them from being deallocated by the memory management system, even if they are no longer needed.

Here’s an example of a closure that can create a strong reference cycle:

class MyClass {
var closure: (() -> Void)?

func setupClosure() {
closure = {
self.doSomething()
}
}

func doSomething() {
print("Did something")
}

deinit {
print("Deinitialized")
}
}

var obj: MyClass? = MyClass()
obj?.setupClosure()
obj = nil // Deinitialized will not be printed

In this example, MyClass has a closure property that captures self and calls the doSomething method. If obj is deallocated while the closure still exists, a retain cycle will occur because the closure has a strong reference to self, which in turn has a strong reference to the closure.

To avoid this, we can use [weak self] in the closure's capture list to create a weak reference to self. Here's how the modified code would look:

class MyClass {
var closure: (() -> Void)?

func setupClosure() {
closure = { [weak self] in
self?.doSomething()
}
}

func doSomething() {
print("Did something")
}

deinit {
print("Deinitialized")
}
}

var obj: MyClass? = MyClass()
obj?.setupClosure()
obj = nil // Deinitialized will be printed

In this modified code, the closure captures self weakly using [weak self], which means that the closure does not create a strong reference to self. Instead, the closure uses optional chaining (self?.doSomething()) to call the doSomething method on self only if self still exists. If self has been deallocated, the closure will not be called.

Using [weak self] in closures is a common technique in Swift to avoid retain cycles and memory leaks.

One common scenario where retain cycles can occur in Swift is when using closures as callbacks or completion handlers in asynchronous operations. For example, if you have a view controller that downloads data from a server, and you use a closure as a completion handler, the closure may hold a strong reference to the view controller, which can cause a retain cycle if the view controller also holds a strong reference to the closure.

To avoid retain cycles in Swift closures, you can use [weak self] or [unowned self] in the closure's capture list to create a weak reference to self. Using [weak self] creates an optional reference to self, which means that the closure will automatically become nil if self is deallocated. Using [unowned self] creates an implicitly unwrapped optional reference to self, which means that the closure will crash if self is deallocated before the closure is called.

class MyClass {
var closure: (() -> Void)?

func setupClosure() {
closure = { [unowned self] in
self.doSomething()
}
}

func doSomething() {
print("Did something")
}

deinit {
print("Deinitialized")
}
}

var obj: MyClass? = MyClass()
obj?.setupClosure()
obj = nil // Deinitialized will be printed

In this example, the closure captures self using [unowned self], which creates an unowned reference to self. This means that the closure assumes that self will always exist when the closure is called, and will crash if self has been deallocated. Using [unowned self] can be a good option if you're sure that self will always exist when the closure is called, and you want to avoid the overhead of optional binding. However, if there's any chance that self could be deallocated before the closure is called, you should use [weak self] instead.

Scenario:

In Swift, closures capture their context. In other words, anything “mentioned” inside the closure will be strongly referenced by the closure.

If you have a closure inside a class that uses self, the closure will keep a strong reference to self as long as the closure lives in memory.

Now, imagine that self refers to a view controller and there is a closure that performs an operation without [weak self].

This creates a strong reference cycle between the closure and the view controller.

If you now pop the view controller off the navigation stack, it will be kept alive by the closure that strongly references it. In other words, the dismissed view controller continues to perform even though it was deleted. This can lead to unexpected behavior, such as exceptions or memory crashes.

Key Points:

  1. [weak self] prevents closures from causing memory leaks in your application. This is because when you use [weak self], you tell the compiler to create a weak reference to self. In other words, the ARC can release self from memory when necessary.
  2. Weak references are always declared as optional variables as they can automatically be set to nil by ARC when its reference is deallocated
  3. It’s best practice to always use weak combined with self inside closures to avoid retain cycles. However, this is only needed if self also retains the closure. By adding weak by default you probably end up working with optionals in a lot of cases while it’s actually not needed.
  4. When an object or value is referenced within an escaping closure, it gets captured in order to be available when that closure is executed. By default, when an object gets captured, it will be retained using a strong reference — which in the case of self could lead to retain cycles in certain situations.
  5. When using [weak self] in closures, you should always use optional binding (if let) or optional chaining (self?.method()) to access self, because the reference to self may be nil.
  6. When using [weak self] in closures, you should also make sure to use guard let self = self else { return } or a similar check to unwrap the optional reference to self before using it, to avoid runtime errors.

When to use [weak self]

  • Using [weak self] is only required within situations in which capturing self strongly would end up causing a retain cycle, for example when self is being captured within a closure that’s also ultimately retained by that same object.
  • Using [weak self] can also be a good idea when working with closures that will be stored for a longer period of time, as capturing an object strongly within such a closure will cause it to remain in memory for that same amount of time.
  • In all other situations, using [weak self] is optional, but there’s typically no harm in adding it — unless we want to capture self strongly for some reason.

--

--

No responses yet