[weak self] in Swift
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:
- [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.
- Weak references are always declared as optional variables as they can automatically be set to
nil
by ARC when its reference is deallocated - 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.
- 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. - When using
[weak self]
in closures, you should always use optional binding (if let
) or optional chaining (self?.method()
) to accessself
, because the reference toself
may be nil. - When using
[weak self]
in closures, you should also make sure to useguard let self = self else { return }
or a similar check to unwrap the optional reference toself
before using it, to avoid runtime errors.
When to use [weak self]
- Using
[weak self]
is only required within situations in which capturingself
strongly would end up causing a retain cycle, for example whenself
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 captureself
strongly for some reason.