kelan.io

The Weak/Strong Dance in Swift

We’re all familiar with the “weak/strong dance” in Objective-C. But I am wondering what the best practice is for doing it in Swift.

For some context, here’s a (somewhat contrived) example where you need to capture a weak reference in a closure, otherwise you get a retain cycle.

class C {
    let name: String
    var block: (() -> Void)?

    init(name: String) {
        self.name = name
        block = {
            self.doSomething()
        }
    }
    deinit { print("Destroying \(name)") }
    func doSomething() { print("Doing something for \(name)") }
}

var c = C(name: "one")
print(c.name)
c = C(name: "two")
print(c.name)

Output:

one
two

Note that deinit never happens for the first object (because “Destroying one” was never printed), even when c is changed to point at a new instance. That’s because of the retain cycle.

Swift’s capture lists do a nice job of letting you only capture self weakly inside a closure, to avoid the retain cycle. That’s half the battle.

Using the Capture List

class C {
    let name: String
    var block: (() -> Void)?

    init(name: String) {
        self.name = name
        block = { [weak self] in  // <-- Here is the change
            self?.doSomething()
        }
    }
    deinit { print("Destroying \(name)") }
    func doSomething() { print("Doing something for \(name)") }
}

var c = C(name: "one")
print(c.name)
c = C(name: "two")
print(c.name)

Now it outputs:

one
Destroying one
two

Much better.

Using self inside the Closure

A nice detail about using [weak self] is that self becomes an Optional inside of the closure. You can see this by the fact that we had to change to self?.doSomething() in the preceding example.

However, what if you have multiple steps that want to use self inside the closure. Since it’s a weak reference, it can go away at any time, even between 2 subsequent uses. Here’s an example, with a slightly different set up, to focus on the closure.

class C {
    deinit { println("Destroying C") }
    func log(msg: String) { println(msg) }
    func doClosure() {
        dispatch_async(dispatch_get_global_queue(0, 0)) { [weak self] in
            self?.log("before sleep")
            usleep(500)
            self?.log("after sleep")
        }
    }
}

var c: C? = C()  // Optional, so we can set it to nil
c?.doClosure()

dispatch_async(dispatch_get_global_queue(0, 0)) {
    usleep(100)
    c = nil  // This will dealloc c
}

dispatch_main()

Output:

before sleep
Destroying C

It doesn’t print after sleep, because self? is nil by then.

This can lead to some very subtle and hard-to-find bugs. So, it’s common to convert the reference back to a strong one inside the closure, to make sure that once the closure starts executing, that self will stay alive until the end. In fact, clang even has warnings if you forget to do this in Objective-C.

But, as I mentioned at the start of this post, I’m not sure what the best practice is in Swift for this “back to strong” part.

A Few Ideas to get a Strong Reference

Use if let

func doClosure() {
    dispatch_async(dispatch_get_global_queue(0, 0)) { [weak self] in
        if let strongSelf = self {  // <-- This is the interesting part
            strongSelf.log("before sleep")
            usleep(500)
            strongSelf.log("after sleep")
        }
    }
}

// or in Swift 2, using `guard let`:
dispatch_async(dispatch_get_global_queue(0, 0)) { [weak self] in
    guard let strongSelf = self else { return }  // <-- This is the interesting part
    strongSelf.log("before sleep")
    usleep(500)
    strongSelf.log("after sleep")
}

Output:

before sleep
after sleep
Destroying C

Pros:

Cons:

Use withExtendedLifetime

Swift’s standard library has a function called withExtendedLifetime(), that is similar in spirit to what we’re trying to accomplish here.

/// Evaluate `f()` and return its result, ensuring that `x` is not
/// destroyed before f returns.
func withExtendedLifetime<T, Result>(x: T, @noescape _ f: () -> Result) -> Result

So, we could use that instead:

func doClosure() {
    dispatch_async(dispatch_get_global_queue(0, 0)) { [weak self] in
        withExtendedLifetime(self) {
            self!.log("before sleep")
            usleep(500)
            self!.log("after sleep")
        }
    }
}

Pros:

Cons:

Make a custom withExtendedLifetime()

A suggestion from @jtbandes was to make a custom withExtendedLifetime() that passes the value to the closure it takes:

 extension Optional {
    func withExtendedLifetime(body: T -> Void) {
        if let strongSelf = self {
            body(strongSelf)
        }
    }
}

// Then:
func doClosure() {
    dispatch_async(dispatch_get_global_queue(0, 0)) { [weak self] () -> Void in
        self.withExtendedLifetime {
            $0.log("before sleep")
            usleep(500)
            $0.log("after sleep")
        }
        return
    }
}

Pros:

Cons:

Other?

If you have any better ideas, let me know!.