[swift-evolution] [Last Call] Allow using optional binding to upgrade self from a weak to strong reference
Vanderlei Martinelli
vmartinelli at alecrim.com
Wed Feb 24 12:40:18 CST 2016
Great!
I agree with the text and the proposal. :-)
-Van
On Tue, Feb 23, 2016 at 6:11 PM, Evan Maloney <emaloney at gilt.com> wrote:
> Hello,
>
> This draft has been refined through at least 6 rounds of discussion, so
> I'm soliciting final comments before submitting a pull request:
>
> https://gist.github.com/emaloney/4bfcb21aaced15af8884
>
> Please let me know what you think!
>
> Thanks,
> E.
>
> ---
>
> Allow using optional binding to upgrade self from a weak to strong
> reference
>
> - Proposal: TBD
> - Author: Evan Maloney <https://github.com/emaloney>
> - Status: *Draft*
> - Review manager: TBD
>
> <https://gist.github.com/emaloney/4bfcb21aaced15af8884#introduction>
> Introduction
>
> When working with escaping Swift closures, it is a common pattern to have
> the closure capture self weakly to avoid creating an object reference
> cycle.
>
> For example, let’s say you have a view controller that displays the result
> of a network operation. When the view controller is placed onscreen, it
> starts the operation and provides a closure to be executed upon completion.
>
> The fact that a network operation may be in-flight should not prevent user
> from navigating away from that view controller. Similarly, we don’t want a
> pending network operation to prevent our view controller from being
> deallocated after it goes offscreen. In other words, we only care about the
> network operation while the view controller is alive; once the view
> controller has been deallocated, we can safely ignore the result of any
> network request it initiated.
>
> To achieve this, the networking code might look something like:
>
> networkRequest.fetchData() { [weak self] result in
> guard let strongSelf = self else { return }
>
> switch result {
> case .Succeeded(let data):
> strongSelf.processData(data)
>
> case .Failed(let err):
> strongSelf.handleError(err)
> }
> }
>
> When it comes time to execute this closure, the guard statement
> effectively asks the question, “Is the view controller represented by self still
> alive?” If the answer is no, the guard forces a return and the rest of the
> closure does not execute.
>
> If self *is* still alive, then the weakly-captured self will be non-nil and
> it will be converted into a strong reference held by strongSelf for the
> duration of the closure’s execution.
>
> When the closure finishes, strongSelf goes away, once again making the
> view controller represented by self eligible for deallocation if no other
> references are held.
> <https://gist.github.com/emaloney/4bfcb21aaced15af8884#the-problem>The
> Problem
>
> The only available mechanism for upgrading a weak self to a strong
> reference requires the creation of a self-like variable with an arbitrary
> name—in the example above, strongSelf.
>
> Because there is no compiler-level mechanism for enforcing a consistent
> name across an entire codebase, in some instances strongSelf may be ss or
> it may be s or it may be a random sequence of characters that captures
> the developer’s mood of the moment.
>
> This lack of consistency adds noise to the codebase, and makes code harder
> to reason about, especially in cases where the strong reference is held by
> a variable with a name more cryptic than strongSelf.
>
> Being able to upgrade self from a weak reference to a strong reference
> while retaining the name self would be ideal, and it would be consistent
> with the existing Swift convention of optional binding that reuses the name
> of the optional variable, eg.:
>
> // foo is an optional hereif let foo = foo {
> // foo is non-optional here;
> // the optional foo is masked within this scope
> }// foo is once again an optional here
>
> <https://gist.github.com/emaloney/4bfcb21aaced15af8884#proposed-solution>Proposed
> Solution
>
> The proposed solution entails allowing self to be upgraded from a weak
> reference to a strong reference using optional binding.
>
> In any scope where self is a weak reference, the compiler will accept an
> if or guard statement containing an optional binding that upgrades self to
> a strong reference.
>
> This would allow self to keep its meaningful name instead of being
> renamed to something arbitrary.
>
> With this feature, the code above could be rewritten as:
>
> networkRequest.fetchData() { [weak self] result in
> guard let self = self else { return }
>
> switch result {
> case .Succeeded(let data):
> self.processData(data)
>
> case .Failed(let err):
> self.handleError(err)
> }
> }
>
> The following would also be legal:
>
> networkRequest.fetchData() { [weak self] result in
> if let self = self {
> switch result {
> case .Succeeded(let data):
> self.processData(data)
>
> case .Failed(let err):
> self.handleError(err)
> }
> }
> }
>
> <https://gist.github.com/emaloney/4bfcb21aaced15af8884#behavior>Behavior
>
> Regardless of which notation is used for this feature, the behavior is the
> same:
>
> -
>
> The strong self can only be assigned from the optional self resulting
> from a weak capture in a closure.
> -
>
> Once bound, the strong self follows the same scoping rules as any
> other optionally-bound variable.
> -
>
> While the strong self is in scope, it masks the weak self variable. If
> the strong reference to self goes out of scope before the weak self reference
> does, the weak self will once again be visible to code.
>
> <https://gist.github.com/emaloney/4bfcb21aaced15af8884#restrictions>
> Restrictions
>
> To ensure safety, the compiler will enforce certain restrictions on the
> use of this feature:
>
> -
>
> Attempting to use this feature in a context where self is not a weak
> reference will cause a compiler error.
> -
>
> Binding of self may only be used with let; attempting to bind self to
> a var is an error. (Because this feature only works with object
> references and not value types, this restriction does not affect the
> mutability of self.)
>
>
> <https://gist.github.com/emaloney/4bfcb21aaced15af8884#impact-on-existing-code>Impact
> on Existing Code
>
> None, since this does not affect any existing constructs. Implementation
> of this proposal will not result in any code breakage.
>
> <https://gist.github.com/emaloney/4bfcb21aaced15af8884#alternatives-considered>Alternatives
> Considered
> <https://gist.github.com/emaloney/4bfcb21aaced15af8884#status-quo>Status
> quo
>
> The primary alternative is to do nothing, requiring developers to add
> boilerplate guard code and handle upgrading the weak-to-strong references
> manually.
>
> As stated above, this leads to needless boilerplate that can easily be
> factored out by the compiler. Also, the use of a self-like variable with
> an arbitrary name makes it more difficult to exhaustively find such uses in
> large projects.
>
> Finally, the need to declare and use alternate names to capture values
> that already have existing names adds visual clutter to code and serves to
> obscure the code’s original intent, making it harder to reason about.
>
> <https://gist.github.com/emaloney/4bfcb21aaced15af8884#relying-on-a-compiler-bug>Relying
> on a compiler bug
>
> There is a bug in current versions of the Swift compiler that allow self to
> be assigned when the word is surrounded by backticks.
>
> This bug causes the following code to compile and work:
>
> guard let `self` = self else {
> return
> }
>
> Apple’s Chris Lattner has stated that “this is a compiler bug
> <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160118/007425.html>
> ”.
>
> Therefore, we should not rely on this “feature” to work in the future,
> because the bug will (presumably) be fixed eventually.
>
> <https://gist.github.com/emaloney/4bfcb21aaced15af8884#adding-a-new-guard-capture-type>Adding
> a new guard capture type
>
> An alternate to this proposal involves adding a new capture type, called
> guard <https://gist.github.com/emaloney/d34ac9b134ece7c60440>, which
> would automatically handle upgrading self (and other references) from
> weak to strong.
>
> Although the alternate proposal received a favorable response from the
> Swift Evolution mailing list, the community seemed split between the
> approach outlined in that proposal, and the one outlined here.
> <https://gist.github.com/emaloney/4bfcb21aaced15af8884#citations>Citations
>
> Variations on this proposal were discussed earlier in the following
> swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
> threads:
>
> - Wanted: syntactic sugar for [weak self] callbacks
> <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160201/008713.html>
> - Allowing guard let self = self else { … } for weakly captured self
> in a closure.
> <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160201/009023.html>
> - [Draft Proposal] A simplified notation for avoiding the weak/strong
> dance with closure capture lists
> <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160201/009241.html>
> - [Proposal Update 1] A simplified notation for avoiding the
> weak/strong dance with closure capture lists
> <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160208/009972.html>
> - [Proposal] Allow upgrading weak self to strong self by assignment
> <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160215/010691.html>
> - [Proposal] Allow using optional binding to upgrade self from a weak
> to strong reference
> <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160215/010759.html>
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160224/dfe424a0/attachment.html>
More information about the swift-evolution
mailing list