Weak vs Strong Unowned in Swift
I often find myself worrying about retain cycles in my code. I feel like this is a common concern amongst others as well. I don't know about you, but it seems like I am constantly hearing "When am I supposed to use weak? And what the hell is this 'unowned' crap?!" The issue we find is that we know to use strong, weak, and unowned specifiers in our swift code to avoid retain cycles, but we don't quite know which specifier to use. Fortunately, I happen to know what they are AND when to use them! I hope this guide helps you to learn when and where to use them on your own.
Strong references are used almost everywhere in Swift. In fact, the declaration of a property is strong by default! Generally, we are safe to use strong references when the hierarchy relationships of objects are linear. When a hierarchy of strong references flow from parent to child, then it's always ok to use strong references.
Here is an example of strong references at play.
Similarly, in animation blocks, the reference hierarchy is similar as well:
What about when a child wants to reference a parent? Here is where we want to use weak and unowned references.
In Swift, all weak references are non-constant
For example, this won't compile:
Important places to use weak variables are in cases where you have potential retain cycles. A retain cycle is what happens when two objects both have strong references to each other. If 2 objects have strong references to each other, ARC will not generate the appropriate
LET'S GET STARTED
ARC
ARC is a compile time feature that is Apple's version of automated memory management. It stands for Automatic Reference Counting. This means that it only frees up memory for objects when there are zero strong references to them.STRONG
Let's start off with what a strong reference is. It's essentially a normal reference (pointer and all), but it's special in it's own right in that it protects the referred object from getting deallocated by ARC by increasing it's retain count by 1. In essence, as long asanything has a strong reference to an object, it will not be deallocated. This is important to remember for later when I explain retain cycles and stuff.Strong references are used almost everywhere in Swift. In fact, the declaration of a property is strong by default! Generally, we are safe to use strong references when the hierarchy relationships of objects are linear. When a hierarchy of strong references flow from parent to child, then it's always ok to use strong references.
Here is an example of strong references at play.
Here we have a linear hierarchy at play.class Kraken { let tentacle = Tentacle() //strong reference to child. } class Tentacle { let sucker = Sucker() //strong reference to child }
class Sucker {}
Kraken
has a strong reference to a Tentacle
instance which has a strong reference to a Sucker
instance. The flow goes from Parent (Kraken
) all the way down to child (Sucker
).Similarly, in animation blocks, the reference hierarchy is similar as well:
UIView.animate(withDuration: 0.3) {
self.view.alpha = 0.0
}
Since animateWithDuration
is a static method on UIView
, the closure here is the parent and self is the child.What about when a child wants to reference a parent? Here is where we want to use weak and unowned references.
WEAK AND UNOWNED REFERENCES
WEAK
A weak reference is just a pointer to an object that doesn't protect the object from being deallocated by ARC. While strong references increase the retain count of an object by 1, weak references do not. In addition, weak references zero out the pointer to your object when it successfully deallocates. This ensures that when you access a weak reference, it will either be a valid object, or nil.In Swift, all weak references are non-constant
Optionals
(think var
vs. let
) because the reference can and will be mutated to nil when there is no longer anything holding a strong reference to it.For example, this won't compile:
class Kraken {
//let is a constant! All weak variables MUST be mutable.
weak let tentacle = Tentacle()
}
because tentacle
is a let
constant. Let
constants by definition cannot be mutated at runtime. Since weak variables can be nil if nobody holds a strong reference to them, the Swift compiler requires you to have weak variables as var
s.Important places to use weak variables are in cases where you have potential retain cycles. A retain cycle is what happens when two objects both have strong references to each other. If 2 objects have strong references to each other, ARC will not generate the appropriate
release
message code on each instance since they are keeping each other alive. Here's a neat little image from Apple that nicely illustrates this:
A perfect example of this is with the (fairly new)
Here,
Other gotchas where this could happen is in places like
The fix is to use a weak reference to
NSNotification
APIs. Take a look at the codes:class Kraken {
var notificationObserver: ((Notification) -> Void)?
init() {
notificationObserver = NotificationCenter.default.addObserver(forName: "humanEnteredKrakensLair", object: nil, queue: .main) { notification in
self.eatHuman()
}
}
deinit {
NotificationCenter.default.removeObserver(notificationObserver)
}
}
At this point we have a retain cycle. You see, Closures in swift behave exactly like blocks in Objective-C. If any variable is declared outside of the closure's scope, referencing that variable inside the closure's scope creates another strong reference to that object. The only exceptions to this are variables that use value semantics such as Ints
, Strings
, Arrays
, and Dictionaries
in Swift.Here,
NotificationCenter
retains a closure that captures self strongly when you call eatHuman()
. Best practice says that you clear out notification observers in the deinit function. The problem here is that we don’t clear out the block until deinit
, but deinit
won’t ever be called by ARC because the closure has a strong reference to the Kraken instance!Other gotchas where this could happen is in places like
NSTimers
and NSThread.
The fix is to use a weak reference to
self
in the closure's capture list. This breaks the strong reference cycle. At this point, our object reference graph will look like this:
Changing self to weak won't increase
To use
Here's why:
AT THE SAME TIME
To use a weak variable in this scenario, we add a weak specifier to the beginning of the delegate declaration:
At this point, we have to use a class protocol to mark the delegate property as weak by having our protocol inherit
self
's retain count by 1, therefore allowing to ARC to deallocate it properly at the correct time.To use
weak
and unowned
variables in a closure, you use the [] in syntax inside of the closure's body. Example:let closure = { [weak self] in
self?.doSomething() //Remember, all weak variables are Optionals!
}
Why is the weak self inside of square brackets? That looks weird! In Swift, we see square brackets and we think Arrays
. Well guess what? You can specify multiple capture values in a closure! Example://Look at that sweet, sweet Array of capture values.
let closure = { [weak self, unowned krakenInstance] in
self?.doSomething() //weak variables are Optionals!
krakenInstance.eatMoreHumans() //unowned variables are not.
}
That looks more like an Array
right? So, now you know why capture values are in square brackets. So, now, using what we've learned so far, we can fix the retain cycle in the notification code we posted above by adding [weak self] to the closure's capture list:NotificationCenter.default.addObserver(forName: "humanEnteredKrakensLair", object: nil, queue: .main) { [weak self] notification in //The retain cycle is fixed by using capture lists!
self?.eatHuman() //self is now an optional!
}
One other place we need to use weak and unowned variables is when using protocols to employ delegation amongst classes in Swift, since classes use reference semantics. In Swift, structs and enums can conform to protocols as well, but they use value semantics. If a parent class uses delegation with a child class like so:class Kraken: LossOfLimbDelegate {
let tentacle = Tentacle()
init() {
tentacle.delegate = self
}
func limbHasBeenLost() {
startCrying()
}
}
protocol LossOfLimbDelegate {
func limbHasBeenLost()
}
class Tentacle {
var delegate: LossOfLimbDelegate?
func cutOffTentacle() {
delegate?.limbHasBeenLost()
}
}
Then we need to use a weak variable.Here's why:
Tentacle
in this case holds a strong reference to Kraken
in the form of it's delegate
property.AT THE SAME TIME
Kraken
holds a strong reference to Tentacle
in it's tentacle
property.To use a weak variable in this scenario, we add a weak specifier to the beginning of the delegate declaration:
weak var delegate: LossOfLimbDelegate?
What's that you say? Doing this won't compile?! Well, the problem is because non class type protocols cannot be marked as weak.At this point, we have to use a class protocol to mark the delegate property as weak by having our protocol inherit
:class
.protocol LossOfLimbDelegate: class { //The protocol now inherits class
func limbHasBeenLost()
}
When do we not use :class
? Well according to Apple:
Essentially, if you have a reference hierarchy exactly like the one I showed above, you use
This is where it gets a little confusing. Weak and unowned references both do not increase retain counts. They can both be used to break retain cycles. So when do we use them?! According to Apple's docs:
:class
. In struct and enum situations, there is no need for :class
because structs and enums use value semantics while classes use reference semantics.UNOWNED
Weak and unowned references behave similarly but are NOT the same. Unowned references, like weak references, do notincrease the retain count of the object being referred. However, in Swift, an unowned reference has the added benefit of not being an Optional. This makes them easier to manage rather than resorting to using optional binding. This is not unlike Implicitly Unwrapped Optionals . In addition, unowned references are non-zeroing. This means that when the object is deallocated, it does not zero out the pointer. This means that use of unowned references can, in some cases, lead to dangling pointers. For you nerds out there that remember the Objective-C days like I do, unowned references map tounsafe_unretained
references.This is where it gets a little confusing. Weak and unowned references both do not increase retain counts. They can both be used to break retain cycles. So when do we use them?! According to Apple's docs:
Well there you have it: Just like an implicitly unwrapped optional, If you can guarantee that the reference will not be nil at its point of use, use unowned. If not, then you should be using weak.
Here's a good example of a class that creates a retain cycle using a closure where the captured self will not be nil:
Apple also says this about unowned references:
Here's a good example of a class that creates a retain cycle using a closure where the captured self will not be nil:
class RetainCycle {
var closure: (() -> Void)!
var string = "Hello"
init() {
closure = {
self.string = "Hello, World!"
}
}
}
//Initialize the class and activate the retain cycle.
let retainCycleInstance = RetainCycle()
retainCycleInstance.closure() //At this point we can guarantee the captured self inside the closure will not be nil. Any further code after this (especially code that alters self's reference) needs to be judged on whether or not unowned still works here.
In this case, the retain cycle comes from the closure capturing self strongly while self has a strong reference to the closure via the closure property. To break this we simply add [unowned self]
to the closure assignment:closure = { [unowned self] in
self.string = "Hello, World!"
}
In this case, we can assume self will never be nil since we call closure immediately after the initialization of the RetainCycle class.Apple also says this about unowned references:
If you know your reference is going to be zeroed out properly and your 2 references are MUTUALLY DEPENDENT on each other (one can't live without the other), then you should prefer unowned over weak, since you aren't going to want to have to deal with the overhead of your program trying to unnecessarily zero your reference pointers.
A really good place to use unowned references is when using self in closure properties that are lazily defined like so:
HOWEVER, this is not to be confused with lazy variables that AREN'T closures such as this:
A really good place to use unowned references is when using self in closure properties that are lazily defined like so:
class Kraken {
let petName = "Krakey-poo"
lazy var businessCardName: (Void) -> String = { [unowned self] in
return "Mr. Kraken AKA " + self.petName
}
}
We need unowned self here to prevent a retain cycle. Kraken
holds on to the businessCardName
closure for it's lifetime and the businessCardName
closure holds on to the Kraken
for it's lifetime. They are mutually dependent, so they will always be deallocated at the same time. Therefore, it satisfies the rules for using unowned!HOWEVER, this is not to be confused with lazy variables that AREN'T closures such as this:
class Kraken {
let petName = "Krakey-poo"
lazy var businessCardName: String = {
return "Mr. Kraken AKA " + self.petName
}()
}
Unowned self
is not needed since nothing actually retains the closure that's called by the lazy variable. The variable simply assigns itself the result of the closure and deallocates the closure (and decrements the captured self's reference count) immediately after it's first use. Here's a screenshot that proves this! (Screenshot taken shamelessly from Алексей in the comments section!)