Using structs and registering closures with a framework? Did you know closures are reference types?
If your closure is registered with a class from a framework and the closure is closing over self then you have a strong reference cycle memory issue. For example, let’s say we have a struct which has an RxSwift class Variable<String>
member called someVariable.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import Foundation import RxSwift struct DoesItLeak { var someState: String = "initial value" var someVariable: Variable<String> = Variable("some stuff") let bag = DisposeBag() mutating func someFoo() { someVariable.subscribeNext { person in self.someState = "something" } .addDisposableTo(bag) } } |
When you give the subscribeNext
the closure, it will store that closure into the framework. In other words, it has a reference to the closure. If you are referring to self in that closure, you have a strong reference cycle. The struct has a Variable
which has a closure that has a reference back to the struct.
Can we just make the self reference a weak reference? No. Not if it’s a struct.
If it’s a class, we can do the following:
1 2 3 4 5 6 7 8 9 10 11 12 |
: class DoesItLeak { var someState: String = "initial value" var someVariable: Variable<String> = Variable("some stuff") let bag = DisposeBag() func someFoo() { someVariable.subscribeNext { [weak self] person in : and the rest |
Note that we are using class
and [weak self]
now. We also took away the mutating
from before the function.
That solves the memory issue! Thanks goes to some helpful humans which include Darren and Marc Khadpe in this Stackoverflow post.
Update: For an example such as this, Carlos García rightfully pointed out that using [unowned self]
would be better. It’s faster and more. Carlos pointed out this article: How Swift Implements Unowned and Weak References and a related Twitter thread.
If you are going to use unowned
, you will want to know when your app can crash when using it. “Weak, Strong, Unowned, Oh My!” – A Guide to References in Swift is a good guide for when to use weak, unowned, and the default strong references.
Hi Mike,
I thought you can’t make self weak or unowned in a closure if it’s a struct, because structs are value types.
So when the closure gets the structs, it gets a copy instead of a reference. That way there’s no leak/reference cycle.
Am I wrong?
Thanks!
Nevermind, I got it!
The copy struct that the variable has, has the same variable referenced as the original struct, so there’s a ref cycle.