Swift structs, closures and Memory Leaks

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.

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:

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.

2 thoughts on “Swift structs, closures and Memory Leaks

  1. Levi says

    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!

    • Levi says

      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.