Give your simulator superpowers

RocketSim: An Essential Developer Tool
as recommended by Apple

Win a Let's visionOS 2024 conference ticket. Join for free

Weak self and unowned self explained in Swift

Weak self and unowned self in Swift for many of us are hard to understand. Although Automatic Reference Counting (ARC) solved a lot for us already, we still need to manage references when we’re not working with value types.

When writing Swift code we often run into situation while writing things like closures. Those situations require us to think carefully about retaining self.

By adding weak by default you probably end up working with optionals in a lot of cases while it’s actually not needed.

What is ARC, retain and release?

We need to start with the foundation to fully understand what weak self and unowned self is doing. It’s best explained by reading through the Automatic Reference Counting in Swift documentation and all comes down to memory management.

Before ARC, we had to manually manage memory and references. This caused a lot of bugs and headaches which many developers can probably tell. The reference count goes up when a new instance retains an object, while it goes down once a reference is released. Memory is freed up as soon as there’s no reference left to an object which means that the object is no longer needed.

In Swift, we need to use weak self and unowned self to give ARC the required information between relationships in our code. Without using weak or unowned you’re basically telling ARC that a certain “strong reference” is needed and you’re preventing the reference count from going to zero. Without correctly using these keywords we possibly retain memory which can cause memory leaks in your app. So-called Strong Reference Cycles or Retain Cycles can occur as well if weak and unowned are not used correctly.

As quoted and later explained in this blog post, it’s good to know that:

Reference counting applies only to instances of classes. Structures and enumerations are value types, not reference types, and are not stored and passed by reference.

When to use weak self?

First of all, weak references are always declared as optional variables as they can automatically be set to nil by ARC when its reference is deallocated. The following two classes are going to help explain when to use a weak reference.

class Blog {
    let name: String
    let url: URL
    var owner: Blogger?

    init(name: String, url: URL) { self.name = name; self.url = url }

    deinit {
        print("Blog \(name) is being deinitialized")
    }
}

class Blogger {
    let name: String
    var blog: Blog?

    init(name: String) { self.name = name }

    deinit {
        print("Blogger \(name) is being deinitialized")
    }
}

As soon as any of these classes is deallocated a message is printed out. In the following code example, we’re defining two instances as optionals following by setting them to nil. Although some of you might expect two print statements, this doesn’t actually happen:

var blog: Blog? = Blog(name: "SwiftLee", url: URL(string: "www.avanderlee.com")!)
var blogger: Blogger? = Blogger(name: "Antoine van der Lee")

blog!.owner = blogger
blogger!.blog = blog

blog = nil
blogger = nil

// Nothing is printed

This is the result of a retain cycle. The blog has a strong reference to its owner and is not willing to release. At the same time, the owner is not willing to free up its blog. The blog does not release its owner who is retaining its blog which is retaining himself which… well, you get the point, it’s an infinite loop: a retain cycle.

Therefore, we need to introduce a weak reference. In this example, only one weak reference is needed as this would already break the loop. For example, we could set a weak reference from the blog to its owner:

class Blog {
    let name: String
    let url: URL
    weak var owner: Blogger?

    init(name: String, url: URL) { self.name = name; self.url = url }

    deinit {
        print("Blog \(name) is being deinitialized")
    }
}

class Blogger {
    let name: String
    var blog: Blog?

    init(name: String) { self.name = name }

    deinit {
        print("Blogger \(name) is being deinitialized")
    }
}

var blog: Blog? = Blog(name: "SwiftLee", url: URL(string: "www.avanderlee.com")!)
var blogger: Blogger? = Blogger(name: "Antoine van der Lee")

blog!.owner = blogger
blogger!.blog = blog

blog = nil
blogger = nil

// Blogger Antoine van der Lee is being deinitialized
// Blog SwiftLee is being deinitialized

But how about weak self?!

I know, this wasn’t an example of weak self. However, it does explain the story!

For many of us, 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.

Say that we introduce a publish method for our blog posts. Note that in this example, we’re “faking” a network request by adding a delay manually.

struct Post {
    let title: String
    var isPublished: Bool = false

    init(title: String) { self.title = title }
}

class Blog {
    let name: String
    let url: URL
    weak var owner: Blogger?

    var publishedPosts: [Post] = []

    init(name: String, url: URL) { self.name = name; self.url = url }

    deinit {
        print("Blog \(name) is being deinitialized")
    }

    func publish(post: Post) {
        /// Faking a network request with this delay:
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
            self.publishedPosts.append(post)
            print("Published post count is now: \(self.publishedPosts.count)")
        }
    }
}

var blog: Blog? = Blog(name: "SwiftLee", url: URL(string: "www.avanderlee.com")!)
var blogger: Blogger? = Blogger(name: "Antoine van der Lee")

blog!.owner = blogger
blogger!.blog = blog

blog!.publish(post: Post(title: "Explaining weak and unowned self"))
blog = nil
blogger = nil

This will print out the following:

// Blogger Antoine van der Lee is being deinitialized
// Published post count is now: 1
// Blog SwiftLee is being deinitialized

You can see that the request is completed before the blog has been released. The strong reference allowed us to finish publishing and to save the post to our published posts.

If we would change the publish method to include a weak reference instead:

func publish(post: Post) {
    /// Faking a network request with this delay:
    DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in
        self?.publishedPosts.append(post)
        print("Published post count is now: \(self?.publishedPosts.count)")
    }
}

We would get the following output:

// Blogger Antoine van der Lee is being deinitialized
// Blog SwiftLee is being deinitialized
// Published post count is now: nil

As the blog has been released before the publishing request has been finished, we will never be able to update our local state of published posts.

Therefore, make sure to not use weak self if there’s work to be done with the referencing instance as soon as the closure gets executed.

Weak references and retain cycles

A retain cycle occurs as soon as a closure is retaining self and self is retaining the closure. If we would have had a variable containing an onPublish closure instead, this could occur:

class Blog {
    let name: String
    let url: URL
    weak var owner: Blogger?

    var publishedPosts: [Post] = []
    var onPublish: ((_ post: Post) -> Void)?

    init(name: String, url: URL) {
        self.name = name
        self.url = url

        // Adding a closure instead to handle published posts
        onPublish = { post in
            self.publishedPosts.append(post)
            print("Published post count is now: \(self.publishedPosts.count)")
        }
    }

    deinit {
        print("Blog \(name) is being deinitialized")
    }

    func publish(post: Post) {
        /// Faking a network request with this delay:
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
            self.onPublish?(post)
        }
    }
}

var blog: Blog? = Blog(name: "SwiftLee", url: URL(string: "www.avanderlee.com")!)
var blogger: Blogger? = Blogger(name: "Antoine van der Lee")

blog!.owner = blogger
blogger!.blog = blog

blog!.publish(post: Post(title: "Explaining weak and unowned self"))
blog = nil
blogger = nil

The closure is retaining the blog while the blog is retaining the closure. This results in the following being printed:

// Blogger Antoine van der Lee is being deinitialized
// Published post count is now: 1

Although everything seems fine with the count of 1, we don’t see the blog and publisher being deinitialized. This is because of the retain cycle and results in memory not being freed up.

Adding a weak reference to our blog instance inside the onPublish method solves our retain cycle:

onPublish = { [weak self] post in
    self?.publishedPosts.append(post)
    print("Published post count is now: \(self?.publishedPosts.count)")
}

And results in the following output:

// Blogger Antoine van der Lee is being deinitialized
// Published post count is now: Optional(1)
// Blog SwiftLee is being deinitialized

The data is saved locally and all instances are released. No more retain cycles!

Finally, to round up this section, it’s good to know that:

Property observers aren’t called when ARC sets a weak reference to nil.

When to use unowned self?

Unlike with weak references, a reference is not turned into an optional while using unowned. Still, both unowned and weak don’t create a strong reference.

Quoted from the Apple documentation:

Use a weak reference whenever it is valid for that reference to become nil at some point during its lifetime. Conversely, use an unowned reference when you know that the reference will never be nil once it has been set during initialization.

In general, be very careful when using unowned. It could be that you’re accessing an instance which is no longer there, causing a crash. The only benefit of using unowned over weak is that you don’t have to deal with optionals. Therefore, using weak is always safer in those scenarios.

Why don’t we need this with value types like structs?

In Swift, we have value types and reference types. This already makes it a bit more clear, as with a reference type you actually have a reference to take care of. This means that you need to manage this relation as strong, weak or unowned. Value types instead keep a unique copy of its data, a unique instance. This means that there’s no need to use a weak reference in multi-threaded environments as there’s no reference, but a unique copy we’re working with.

Are weak and unowned only used with self inside closures?

No, definitely not. You can indicate any property or variable declaration weak or unowned as long as it’s a reference type. Therefore, this could also work:

download(imageURL, completion: { [weak imageViewController] result in
    // ...
})

And you could even reference multiple instances as it’s basically an array:

download(imageURL, completion: { [weak imageViewController, weak imageFinalizer] result in
    // ...
})

Conclusion

Overall, it’s a very hard topic to get your head around. It’s best to start with reading the Swift documentation which goes even more in-depth over this topic. Also, if you’re not sure, use weak over unowned. It could save you from annoying bugs. Furthermore, if there’s work to be done inside the closure, don’t use weak and make sure your code is getting executed.

If you like to improve your Swift knowledge, even more, check out the Swift category page. Feel free to contact me or tweet to me on Twitter if you have any additional tips or feedback.

Thanks!

 

Do you know everything about Swift?

You don't need to master everything, but staying informed is crucial. Join our community of 17,905 developers and stay ahead of the curve:


Featured SwiftLee Jobs

Find your next Swift career step at world-class companies with impressive apps by joining the SwiftLee Talent Collective. I'll match engineers in my collective with exciting app development companies. SwiftLee Jobs