The push-pin principle

I’ve written before on my desire to reconstitute a weak reference in completion handlers, something along the lines of :

guard bind self else { return }

John Estropia writes,

About the weak-strong dance, it’s currently perfectly fine to do

guard let `self` = self else {return}; self.doSomething

We actually made this a rule in our team.

I feel all kinds of odd about this approach, which rests on the notion of ensuring a strong reference to self that persists through the handler’s scope. Once you pass through the guard, the self reference will be valid for that lifetime. What do you think of this work-around, which of course would be improved merely by renaming `self`?

guard let strongSelfReference = self else {return}

(Mike Ash offered another solution that works in existing Swift that I encourage you to peek at. His approach registers items and invokes callbacks.)

Looking forward in terms of Swift Evolution, I’ve recommended enhancements along the lines of:

// self-shadow a weak item
guard bind self else {return}

//and an equivalent for a symbol that doesn't shadow itself
guard bind symbol = someInstance.blah.weakReference else {return}

But don’t hold your breath waiting for bind to appear in Swift. I find `bind` clearer than overloading a constant or variable declaration. It’s garnered no supporters and I’ve reluctantly been letting it go. (Although not so quickly that I didn’t first include it in this post as a last farewell.)

Others suggested integrating a guard into the initial weak capture. Unfortunately [guard self] doesn’t express how scope should exit. Should it return? Throw? Print an error? A separate guard statement is more verbose but it places explicit control in the hands of the developer on exit strategies.

Alternatively, you could introduce a weakstrong capture specifier that’s limited to -> Void closures (thanks Davide De Franceschi and Mike Ash)

[weakstrong self] in

Evan Maloney’s Allow using optional binding to upgrade `self` from a weak to strong reference proposal recommends allowing shadowing self without backticks.

[weak self] result in
guard let self = self else { return }

How would you redesign things? What do you think of the pushpin? Does Swift need to change? If so, how?


  • What about `guard [strong self] else { return }` to mirror the `[weak self]` syntax in the closure?

    • I considered it but it has a bigger footprint on capture lists

  • Wouldn’t it be strongSelfReference.doSomething in code block 3?

  • I’d really like to see a solution that is used as the defaults for a classes methods. That way, I can set the action to either an enclosure with a “strongweak” reference to something, or I can just set a method from self and it would Just WorkTM. Currently you can’t make enclosures weak, and even if you could, an inline enclosure wouldn’t have anything else referencing it. But that’s the kind of notation we need, where the caller is responsible for the availability of the closure and can handle the nil case. Perhaps a strongweak attribute could force the enclosure to be optional, and when a caller tries to unwrap that enclosure, Swift would check if all of it’s strongweak captured objects were still valid, retain them, and return the closure with strong references to it’s captured objects.

  • I usually just take advantage of optional chaining inside of the completion block.

    let block = { [weak self] in

    The downside to optional chaining is if there is some complicated logic then you do not want to waste effort and have to fall back to the guard check.

    On the cases which I need to have a strong reference I would use

    guard strongSelf = self else { return }

    I would prefer to be able to denote in the capture list that I will be doing this as every time I do need it there is the strongSelf dance. Interesting topic.

  • This is probably just me but I actually try and avoid using self as much as possible. Seeing self in most cases alerts me in most cases I need to change it to avoid issues. I normal just do guard let s = self else { return }