Update 07-29-2019: The bug described below is fixed in Xcode 11 so this blog post has become irrelevant. I'm leaving it up for historical purposes.
For the Objective-C veterans in the audience, the strong-self-weak-self dance is a practice mastered early on and one that is used very frequently. There are a lot of different incantations, but the most basic one goes something like this:
1 2 3 4 |
|
Then, if you needed a strong reference to self
again inside the block, you'd
change it to this:
1 2 3 4 5 |
|
Fortunately, this was much easier on day 1 of Swift when using the [weak self]
directive:
1 2 3 4 5 |
|
self
is now weak
inside the closure, making it an optional. Unwrapping it
into strongSelf
makes it a non-optional while still avoiding a retain cycle.
It doesn't feel very Swifty, but it's not terrible.
More recently, it's become known that Swift supports re-binding self
if you
wrap it in backticks. That makes for an arguably much nicer syntax:
1 2 3 4 |
|
This was long considered, and confirmed to be, a hack that worked due to a bug in the compiler, but since it worked and there weren't plans to remove it, people (including us at Lyft) started treating it as a feature.
However, there is one big caveat: the debugger is entirely hosed for anything you do in that closure. Ever seen an error like this in your Xcode console?
1 2 3 |
|
That's because self
was re-bound. This is easy to reproduce: create a new
Xcode project and add the following snippet to viewDidLoad()
:
1 2 3 4 5 6 |
|
When the breakpoint hits, execute (lldb) po description
and you'll see the
error from above. Note that you're not even using self
- merely re-binding
it makes the debugger entirely useless inside that scope.
People with way more knowledge of LLDB than I do can explain this in more detail
(and have), but the gist is that the
debugger doesn't like self
's type changing. At the beginning of the closure
scope, the debugging context assumes that self
's type is Optional
, but it is
then re-bound to a non-optional, which the debugger doesn't know how to handle.
It's actually pretty surprising the compiler supports changing a variable's type
at all.
Because of this problem, at Lyft we have decided to eliminate this pattern
entirely in our codebases, and instead re-bind self
to a variable named
this
.
If you do continue to use this pattern, note that in a
discussion
on the Swift forums many people agreed that re-binding self
should be
supported by the language without the need for backticks. The pull
request was merged shortly after and
with the release of Swift 4.2 in the fall, you'll be able to use guard let self
= self else { return }
(at the cost of losing debugging capabilities!)